oak 0.0.3 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +51 -0
- data/.rubocop.yml +74 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +24 -0
- data/DESIDERATA.md +318 -0
- data/Gemfile +3 -15
- data/LICENSE +22 -0
- data/Makefile +113 -0
- data/README.md +163 -23
- data/Rakefile +6 -47
- data/bin/oak +242 -3
- data/bin/oak.rb +245 -0
- data/lib/oak.rb +1049 -86
- data/lib/oak/version.rb +3 -0
- data/oak.gemspec +29 -65
- metadata +121 -71
- data/.document +0 -5
- data/Gemfile.lock +0 -26
- data/LICENSE.txt +0 -20
- data/VERSION +0 -1
- data/test/files/config/application.rb +0 -3
- data/test/files/config/database.yml +0 -25
- data/test/files/config/initializers/secret_token.rb +0 -7
- data/test/files/dot_gitignore +0 -0
- data/test/helper.rb +0 -29
- data/test/test_oak.rb +0 -44
data/Gemfile
CHANGED
@@ -1,17 +1,5 @@
|
|
1
|
-
source
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
1
|
+
source 'https://rubygems.org'
|
5
2
|
|
6
|
-
|
7
|
-
# Include everything needed to run rake, tests, features, etc.
|
8
|
-
group :development do
|
9
|
-
gem "shoulda", ">= 0"
|
10
|
-
gem "bundler", "~> 1.0.0"
|
11
|
-
gem "jeweler", "~> 1.6.4"
|
12
|
-
gem "rcov", ">= 0"
|
13
|
-
gem 'rdoc', "~> 3.11"
|
14
|
-
end
|
15
|
-
|
16
|
-
gem 'thor', "~> 0.14.6"
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
17
4
|
|
5
|
+
gemspec
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016-2018 ProsperWorks, Inc.
|
4
|
+
Copyright (c) 2018 Copper, Inc.
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/Makefile
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
#
|
2
|
+
# Makefile
|
3
|
+
#
|
4
|
+
# "make test" tests the external behavior of cli utilities which are
|
5
|
+
# not accessible to "rake test".
|
6
|
+
#
|
7
|
+
# author: jhw@prosperworks.com
|
8
|
+
# incept: 2019-09-25
|
9
|
+
#
|
10
|
+
|
11
|
+
.SUFFIXES:
|
12
|
+
SHELL := bash
|
13
|
+
DESTDIR := build
|
14
|
+
|
15
|
+
SOURCES := $(shell find . -type f -name '*.rb')
|
16
|
+
SOURCES += $(shell find . -type f -name 'Gemfile*')
|
17
|
+
SOURCES += $(shell find . -type f -name 'Rakefile')
|
18
|
+
SOURCES += $(shell find . -type f -name '.rubocop.yml')
|
19
|
+
|
20
|
+
.PHONY: all
|
21
|
+
.PHONY: test
|
22
|
+
all: test
|
23
|
+
|
24
|
+
.PHONY: clean
|
25
|
+
clean:
|
26
|
+
rm -rf $(DESTDIR)
|
27
|
+
|
28
|
+
# "make test-rake" performs our traditional Ruby Minitest suite.
|
29
|
+
#
|
30
|
+
.PHONY: test-rake
|
31
|
+
test test-rake: $(DESTDIR)/test-rake.ok
|
32
|
+
$(DESTDIR)/test-rake.ok: $(SOURCES)
|
33
|
+
@mkdir -p $(dir $@)
|
34
|
+
bundle exec rake test
|
35
|
+
@touch $@
|
36
|
+
|
37
|
+
# "make test-shell-basic" tests bin/oak.rb for loadability and basic behavior.
|
38
|
+
#
|
39
|
+
.PHONY: test-shell-basic
|
40
|
+
test test-shell-basic: $(DESTDIR)/test-shell-basic.ok
|
41
|
+
$(DESTDIR)/test-shell-basic.ok: $(SOURCES)
|
42
|
+
@mkdir -p $(dir $@)
|
43
|
+
@echo
|
44
|
+
@echo bin/oak is friendly and self-tests
|
45
|
+
@echo
|
46
|
+
bin/oak --self-test
|
47
|
+
rubydoctest bin/oak.rb
|
48
|
+
bin/oak --help
|
49
|
+
@echo
|
50
|
+
@echo bin/oak can decode its own output in some common cases.
|
51
|
+
@echo
|
52
|
+
set -o pipefail ; echo hello | bin/oak | bin/oak --mode decode-lines | diff <(echo hello) -
|
53
|
+
set -o pipefail ; cat Makefile | bin/oak --mode encode-file | bin/oak --mode decode-lines | diff Makefile -
|
54
|
+
set -o pipefail ; cat Makefile | bin/oak --mode encode-file | bin/oak --mode decode-file | diff Makefile -
|
55
|
+
set -o pipefail ; cat Makefile | bin/oak --mode encode-lines | bin/oak --mode decode-lines | diff -w Makefile -
|
56
|
+
set -o pipefail ; cat .git/index | bin/oak --mode encode-file | bin/oak --mode decode-file | diff .git/index -
|
57
|
+
@echo
|
58
|
+
@echo bin/oak has some kind of pointless crufty modes, too.
|
59
|
+
@echo
|
60
|
+
set -o pipefail ; echo | bin/oak --mode tests > /dev/null
|
61
|
+
set -o pipefail ; echo | bin/oak --mode crazy > /dev/null
|
62
|
+
@echo
|
63
|
+
@echo bin/oak passed all the basic tests.
|
64
|
+
@echo
|
65
|
+
@touch $@
|
66
|
+
|
67
|
+
# This keychain is only used for tests in this Makefile.
|
68
|
+
#
|
69
|
+
export OAK_TEST_KEYS=foo,bar
|
70
|
+
export OAK_TEST_KEY_foo=oak_3CNB_3725491808_52_RjFTQTMyX0qAlJNbIK4fwYY0kh5vNKF5mMpHK-ZBZkfFarRjVPxS_ok
|
71
|
+
export OAK_TEST_KEY_bar=oak_3CNB_201101230_52_RjFTQTMyXxbYlRcFH8JgiFNZMbnlFTAfUyvJCnXgCESpBmav_Etp_ok
|
72
|
+
|
73
|
+
# "make test-shell-encryption" tests the encryption features in bin/oak.
|
74
|
+
#
|
75
|
+
.PHONY: test-shell-encryption
|
76
|
+
test test-shell-encryption: $(DESTDIR)/test-shell-encryption.ok
|
77
|
+
$(DESTDIR)/test-shell-encryption.ok: $(SOURCES)
|
78
|
+
@mkdir -p $(dir $@)
|
79
|
+
set -o pipefail ; bin/oak --key-generate | grep '^oak_3[-_0-9a-zA-Z]*_ok$$'
|
80
|
+
set -o pipefail ; cat Makefile | bin/oak --mode encode-lines --key-chain OAK_TEST --key foo | bin/oak --mode decode-lines --key-chain OAK_TEST | diff -w Makefile -
|
81
|
+
set -o pipefail ; cat Makefile | bin/oak --mode encode-file --key-chain OAK_TEST --key bar | bin/oak --mode decode-file --key-chain OAK_TEST | diff Makefile -
|
82
|
+
set -o pipefail ; bin/oak --key-check | grep 'no --key-chain specified'
|
83
|
+
set -o pipefail ; bin/oak --key-check --key-chain BOGUS_KEY_CHAIN_TEST | grep 'BOGUS_KEY_CHAIN_TEST: no keys found'
|
84
|
+
set -o pipefail ; bin/oak --key-check --key-chain OAK_TEST | grep 'OAK_TEST: found keys: foo bar'
|
85
|
+
@echo
|
86
|
+
@echo We can recognize which key encrypted which OAK string:
|
87
|
+
@echo
|
88
|
+
echo Hello | bin/oak --mode encode-file --key-chain OAK_TEST --key foo | grep '^oak_4foo' > /dev/null
|
89
|
+
echo Hello | bin/oak --mode encode-file --key-chain OAK_TEST --key bar | grep '^oak_4bar' > /dev/null
|
90
|
+
@echo
|
91
|
+
@echo We can recode one from one key to another
|
92
|
+
@echo
|
93
|
+
echo Hello | bin/oak --mode encode-file --key-chain OAK_TEST --key foo | grep '^oak_4foo' > /dev/null
|
94
|
+
echo Hello | bin/oak --mode encode-file --key-chain OAK_TEST --key foo | bin/oak --mode recode-file --key-chain OAK_TEST --key bar | grep '^oak_4bar' > /dev/null
|
95
|
+
@echo
|
96
|
+
@echo Both are decodeable as the original source:
|
97
|
+
@echo
|
98
|
+
echo Hello | bin/oak --mode encode-file --key-chain OAK_TEST --key foo | bin/oak --key-chain OAK_TEST --mode decode-file | diff <(echo Hello) - > /dev/null
|
99
|
+
echo Hello | bin/oak --mode encode-file --key-chain OAK_TEST --key foo | bin/oak --mode recode-file --key-chain OAK_TEST --key bar | bin/oak --key-chain OAK_TEST --mode decode-file | diff <(echo Hello) - > /dev/null
|
100
|
+
@echo
|
101
|
+
@echo bin/oak passed all the encryption tests.
|
102
|
+
@echo
|
103
|
+
@touch $@
|
104
|
+
|
105
|
+
# "make test-rubocop" performs our rubocop checks.
|
106
|
+
#
|
107
|
+
.PHONY: test-rubocop
|
108
|
+
test test-rubocop: $(DESTDIR)/test-rubocop.ok
|
109
|
+
$(DESTDIR)/test-rubocop.ok: $(SOURCES)
|
110
|
+
@mkdir -p $(dir $@)
|
111
|
+
bundle exec rubocop --version
|
112
|
+
bundle exec rubocop --display-cop-names --display-style-guide
|
113
|
+
@touch $@
|
data/README.md
CHANGED
@@ -1,34 +1,174 @@
|
|
1
|
-
|
1
|
+
# oak
|
2
2
|
|
3
|
-
|
3
|
+
OAK is a serialization format for Ruby primitives which supports
|
4
|
+
performance experimentation via a limited suite of built-in
|
5
|
+
compression, checksumming, and encryption options.
|
4
6
|
|
5
|
-
|
7
|
+
OAK supports built-in switchability between common algorithms:
|
6
8
|
|
7
|
-
|
9
|
+
- Checksumming with ZLIB crc32(), SHA1, or none.
|
10
|
+
- Compression with LZ4, ZLIB, BZIP2k, LZMA, or none.
|
11
|
+
- ASCII armor with base64, or none.
|
8
12
|
|
9
|
-
|
13
|
+
OAK also supports optional AES-256-GCM encryption with full 96-bit
|
14
|
+
random IVs and 16 byte auth tags.
|
10
15
|
|
11
|
-
|
16
|
+
OAK is also a compact serialization format for primitive Ruby objects
|
17
|
+
like String, Symbol, Integer, Float, Hash, and Array. OAK
|
18
|
+
serialization supports cyclical structures and distinguishes between
|
19
|
+
String and Symbol, but does not support user-defined types.
|
12
20
|
|
13
|
-
|
14
|
-
So you can modify your apps on master branch as usual, and call
|
21
|
+
In ProsperWorks/ALI, we use OAK at Copper, OAK has found use cases in:
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
- Volatile Redis cache entries, where dynamic control of time-space
|
24
|
+
tradeoffs shaves down hosting costs.
|
25
|
+
- Durable cold archives of larger user data.
|
26
|
+
- Encrypting runtime secrets.
|
19
27
|
|
20
|
-
|
21
|
-
|
22
|
-
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
23
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
24
|
-
* Fork the project
|
25
|
-
* Start a feature/bugfix branch
|
26
|
-
* Commit and push until you are happy with your contribution
|
27
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
28
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
28
|
+
OAK is not human-readable. It is intended to be an interchange or
|
29
|
+
archive format, not as a human interface.
|
29
30
|
|
30
|
-
|
31
|
+
Consider OAK if you are dealing with content at scale and are not
|
32
|
+
quite happy with JSON, YAML, XML, or Ruby's Marshal format. Disregard
|
33
|
+
OAK your needs are all met by JSON.
|
31
34
|
|
32
|
-
|
33
|
-
further details.
|
35
|
+
## Using in Ruby
|
34
36
|
|
37
|
+
```
|
38
|
+
2.1.6 :001 > require 'oak'
|
39
|
+
=> true
|
40
|
+
2.1.6 :002 > h = { 'one' => 1, 'array' => [ true, false ] }
|
41
|
+
=> {"one"=>1, "array"=>[true, false]}
|
42
|
+
2.1.6 :003 > OAK.encode(h)
|
43
|
+
=> "oak_3CNB_2774455364_51_RjdIMl8xXzJfM180U1UzX29uZUkxU1U1X2FycmF5QTJfNV82dGY_ok"
|
44
|
+
2.1.6 :004 > OAK.encode(h,format: :none)
|
45
|
+
=> "oak_3CNN_2774455364_38_F7H2_1_2_3_4SU3_oneI1SU5_arrayA2_5_6tf_ok"
|
46
|
+
2.1.6 :005 > OAK.encode(h, compression: :bzip2, force: true)
|
47
|
+
=> "oak_3CBB_2774455364_106_QlpoOTFBWSZTWag9FGUAAAaPgD-AIWAKAKMBlCAgADFGjIGjTI0Ip-lPRGynomJ-qPMBxIQDw5vmY9SVFxhFj7ZLMSPxdyRThQkKg9FGUA_ok"
|
48
|
+
2.1.6 :006 > OAK.encode(h, compression: :bzip2)
|
49
|
+
=> "oak_3CNB_2774455364_51_RjdIMl8xXzJfM180U1UzX29uZUkxU1U1X2FycmF5QTJfNV82dGY_ok"
|
50
|
+
2.1.6 :007 > OAK.decode('oak_3CNB_2774455364_51_RjdIMl8xXzJfM180U1UzX29uZUkxU1U1X2FycmF5QTJfNV82dGY_ok')
|
51
|
+
=> {"one"=>1, "array"=>[true, false]}
|
52
|
+
```
|
53
|
+
|
54
|
+
## Using in Ruby for Encryption
|
55
|
+
|
56
|
+
```
|
57
|
+
2.1.6 :001 > require 'oak'
|
58
|
+
=> true
|
59
|
+
2.1.6 :002 > key_chain = OAK::KeyChain.new({ 'a' => OAK::Key.new(OAK.random_key), 'b' => OAK::Key.new(OAK.random_key) })
|
60
|
+
=> #<OAK::KeyChain:0x007faa700b8458 @keys={"a"=>#<OAK::Key:0x007faa700b89a8 @key=ELIDED>, "b"=>#<OAK::Key:0x007faa700b86d8 @key=ELIDED>}>
|
61
|
+
2.1.6 :003 > OAK.encode("Hello, World!",key_chain: key_chain, key: 'a')
|
62
|
+
=> "oak_4a_B82_4iauuiFq7XQvcDCwmmXhDxL_Wp3_T765qM-S094uNJ4xtj_DxQuXGSMmqlcxPXz-_cYZdE6bFbEAQCHpiQ_ok"
|
63
|
+
2.1.6 :004 > OAK.encode("Hello, World!",key_chain: key_chain, key: 'a')
|
64
|
+
=> "oak_4a_B82_YeeAEOLPlGchlDkoSnif7G38uJAPaNQ2ozsx6Mcb7PybpsL-ljVmGa5sRbgaqFw4R5iOXNw_sOesolTB4g_ok"
|
65
|
+
2.1.6 :005 > OAK.encode("Hello, World!",key_chain: key_chain, key: 'b')
|
66
|
+
=> "oak_4b_B82_HA1O3v5UrpV81UC0fAaUQ_8tsLUxbMG6bMupcQaZNKMU3XL3Tz9zj8TaVb4nvv3s0UhxFg3q9lmFIplvnQ_ok"
|
67
|
+
2.1.6 :006 > OAK.decode("oak_4b_B82_HA1O3v5UrpV81UC0fAaUQ_8tsLUxbMG6bMupcQaZNKMU3XL3Tz9zj8TaVb4nvv3s0UhxFg3q9lmFIplvnQ_ok",key_chain: key_chain)
|
68
|
+
=> "Hello, World!
|
69
|
+
2.1.6 :007 > OAK.decode("oak_4b_B82_HA1O3v5UrpV81UC0fAaUQ_8tsLUxbMG6bMupcQaZNKMU3XL3Tz9zj8TaVb4nvv3s0UhxFg3q9lmFIplvnQ_ok")
|
70
|
+
OAK::CantTouchThisStringError: key b but no key_chain
|
71
|
+
from ...
|
72
|
+
```
|
73
|
+
|
74
|
+
## Using in Shell
|
75
|
+
|
76
|
+
Non-encrypted OAK default to OAK3. Unencrypted OAK4 is available
|
77
|
+
optionally. They are generally the same size and equivalent:
|
78
|
+
unencrypted OAK4 is generally only used for debugging and getting a
|
79
|
+
peek at what happens "under the encryption".
|
80
|
+
|
81
|
+
```
|
82
|
+
$ echo hello | bin/oak --mode encode-file
|
83
|
+
oak_3CNB_911092726_16_RjFTVTZfaGVsbG8K_ok
|
84
|
+
$ echo oak_3CNB_911092726_16_RjFTVTZfaGVsbG8K_ok | bin/oak --mode decode-file
|
85
|
+
hello
|
86
|
+
$ echo hello | bin/oak --mode encode-file --compression lz4 --force true
|
87
|
+
oak_3C4B_911092726_19_DMBGMVNVNl9oZWxsbwo_ok
|
88
|
+
$ echo oak_3C4B_911092726_19_DMBGMVNVNl9oZWxsbwo_ok | bin/oak --mode decode-file
|
89
|
+
hello
|
90
|
+
$ echo hello | bin/oak.rb --mode encode-lines --redundancy none --format none
|
91
|
+
oak_3NNN_0_11_F1SU5_hello_ok
|
92
|
+
$ echo hello | bin/oak.rb --mode encode-lines --redundancy none --format none --force-oak-4
|
93
|
+
oak_4_N15_NN0_F1SU5_hello_ok
|
94
|
+
```
|
95
|
+
|
96
|
+
## Using in Shell for Encryption
|
97
|
+
|
98
|
+
Encryption is supported by OAK4.
|
99
|
+
|
100
|
+
```
|
101
|
+
$ bin/oak.rb --key-generate
|
102
|
+
oak_3CNB_2975186575_52_RjFTQTMyX00du8vD8WAikhLNgdnaOYtQV6uqyNqRz6modiEcJHOl_ok
|
103
|
+
$ bin/oak.rb --key-generate
|
104
|
+
oak_3CNB_1324948677_52_RjFTQTMyXytCueDDTpEOusKkPMANgaA9zsJuvOend5DCIJWwJdjC_ok
|
105
|
+
$ export OAK_TEST_KEYS=foo,bar
|
106
|
+
$ export OAK_TEST_KEY_foo=oak_3CNB_2975186575_52_RjFTQTMyX00du8vD8WAikhLNgdnaOYtQV6uqyNqRz6modiEcJHOl_ok
|
107
|
+
$ export OAK_TEST_KEY_bar=oak_3CNB_1324948677_52_RjFTQTMyXytCueDDTpEOusKkPMANgaA9zsJuvOend5DCIJWwJdjC_ok
|
108
|
+
$ echo hello | bin/oak.rb --mode encode-lines --redundancy none --key-chain OAK_TEST --key foo
|
109
|
+
oak_4foo_B58_PhG1qWHfosOOWDgqMhVoZlEn6F16XC6KuL_1zN1aLWMmcZZgJ2Dz5XR-ag_ok
|
110
|
+
$ echo hello | bin/oak.rb --mode encode-lines --redundancy none --key-chain OAK_TEST --key foo
|
111
|
+
oak_4foo_B58_ms11iWDHrmwFJwGpNEsWMIXYfapO96e7yvfk5r8G-F1gRzt62FS_JFQbvw_ok
|
112
|
+
$ echo hello | bin/oak.rb --mode encode-lines --redundancy none --key-chain OAK_TEST --key bar
|
113
|
+
oak_4bar_B58_kV6FIE30v6xgdKwyzdmpxVzNCU2eWjt7ZiZTWUHsQxXG3cC8u0-VoE0hmQ_ok
|
114
|
+
$ echo oak_4foo_B58_PhG1qWHfosOOWDgqMhVoZlEn6F16XC6KuL_1zN1aLWMmcZZgJ2Dz5XR-ag_ok | bin/oak.rb --mode decode-lines --key-chain OAK_TEST
|
115
|
+
hello
|
116
|
+
$ echo oak_4foo_B58_ms11iWDHrmwFJwGpNEsWMIXYfapO96e7yvfk5r8G-F1gRzt62FS_JFQbvw_ok | bin/oak.rb --mode decode-lines --key-chain OAK_TEST
|
117
|
+
hello
|
118
|
+
$ echo oak_4bar_B58_kV6FIE30v6xgdKwyzdmpxVzNCU2eWjt7ZiZTWUHsQxXG3cC8u0-VoE0hmQ_ok | bin/oak.rb --mode decode-lines --key-chain OAK_TEST
|
119
|
+
hello
|
120
|
+
```
|
121
|
+
OAK4 supports one and only one encryption algorithm and mode of
|
122
|
+
operation:
|
123
|
+
|
124
|
+
- AES-256-GCM
|
125
|
+
- 128 bits of security
|
126
|
+
- 256-bit keys (32 bytes)
|
127
|
+
- 96-bit IVs (12 bytes)
|
128
|
+
- 128-bit auth_tags (16 bytes)
|
129
|
+
- Random IV ("Initialization Vector") used for each encryption operation.
|
130
|
+
- All headers are authenticated.
|
131
|
+
- All headers which are not required for decryption are encrypted.
|
132
|
+
|
133
|
+
This is the only encryption option supported by OAK4.
|
134
|
+
|
135
|
+
Because the GCM ([Galois/Counter Mode
|
136
|
+
](https://en.wikipedia.org/wiki/Galois%2FCounter_Mode)) mode of
|
137
|
+
operation is an [authenticated
|
138
|
+
encryption](https://en.wikipedia.org/wiki/Authenticated_encryption),
|
139
|
+
use of `--redundancy none` with encrypted OAK strings is recommended.
|
140
|
+
The authentication of GCM is more than adequate to detect accidental
|
141
|
+
transmission errors. This recommendation may become the default in a
|
142
|
+
future version.
|
143
|
+
|
144
|
+
|
145
|
+
## Further Reading
|
146
|
+
|
147
|
+
For more details.
|
148
|
+
|
149
|
+
- [Changelog](CHANGELOG.md)
|
150
|
+
- [Design Desiderata](DESIDERATA.md)
|
151
|
+
|
152
|
+
## TODO: packaging
|
153
|
+
|
154
|
+
- import design docs here
|
155
|
+
- https://docs.google.com/document/d/10HVWuQzCw1Whc-czDChwsWPEZRLfyPS7F-dkjHWsIs4
|
156
|
+
- https://docs.google.com/document/d/1J7GBEJUPI3UeftJ4C-w3pbBUzk1pU0Sr6zcXufEi7NI
|
157
|
+
- https://docs.google.com/document/d/1SeOO18uqdDtHuB8tZ4-_2sql0Yiaco5J1PWdiu6gmAY
|
158
|
+
- edit comments down
|
159
|
+
- rdoc
|
160
|
+
|
161
|
+
## Possible Future Directions for the Format
|
162
|
+
|
163
|
+
- Float representation.
|
164
|
+
- Manifest precision?
|
165
|
+
- Limited precision?
|
166
|
+
- But somehow do better than just `Float#to_s`!
|
167
|
+
- Streamability and embedability.
|
168
|
+
- Support encoders and decoders which have only fixed-size buffers.
|
169
|
+
- Portability.
|
170
|
+
- Was support for Symbols distinct from Strings too Ruby-esque?
|
171
|
+
- Native implementation.
|
172
|
+
- Error-correction coding.
|
173
|
+
- There seem to be little or no "standard" algorithms out there, at
|
174
|
+
least not as used by the Ruby community.
|
data/Rakefile
CHANGED
@@ -1,53 +1,12 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'rubygems'
|
4
|
-
require 'bundler'
|
5
|
-
|
6
|
-
Bundler.setup(:default, :development)
|
7
|
-
rescue Bundler::BundlerError => e
|
8
|
-
$stderr.puts e.message
|
9
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
-
exit e.status_code
|
11
|
-
end
|
12
|
-
require 'rake'
|
13
|
-
|
14
|
-
require 'jeweler'
|
15
|
-
Jeweler::Tasks.new do |gem|
|
16
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
-
gem.name = "oak"
|
18
|
-
gem.homepage = "http://github.com/imonyse/oak"
|
19
|
-
gem.license = "MIT"
|
20
|
-
gem.summary = %Q{Oak is a tool that helps making your newly created rails app open source ready.}
|
21
|
-
gem.description = %Q{When pushing your rails app to a public repository, it is often neccessary to find a way protecting you secret token and other private information. Oak helps you on these tasks and assumes you use git as the version control system}
|
22
|
-
gem.email = "imonyse@gmail.com"
|
23
|
-
gem.authors = ["Huang Wei"]
|
24
|
-
# dependencies defined in Gemfile
|
25
|
-
end
|
26
|
-
Jeweler::RubygemsDotOrgTasks.new
|
27
|
-
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'bundler/gem_tasks'
|
28
4
|
require 'rake/testtask'
|
29
|
-
Rake::TestTask.new(:test) do |test|
|
30
|
-
test.libs << 'lib' << 'test'
|
31
|
-
test.pattern = 'test/**/test_*.rb'
|
32
|
-
test.verbose = true
|
33
|
-
end
|
34
5
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
test.verbose = true
|
40
|
-
test.rcov_opts << '--exclude "gems/*"'
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << 'test'
|
8
|
+
t.libs << 'lib'
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
41
10
|
end
|
42
11
|
|
43
12
|
task :default => :test
|
44
|
-
|
45
|
-
require 'rdoc/task'
|
46
|
-
RDoc::Task.new do |rdoc|
|
47
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
-
|
49
|
-
rdoc.rdoc_dir = 'rdoc'
|
50
|
-
rdoc.title = "oak #{version}"
|
51
|
-
rdoc.rdoc_files.include('README*')
|
52
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
-
end
|
data/bin/oak
CHANGED
@@ -1,6 +1,245 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# oak.rb: cli driver for encoding strings in the OAK format.
|
4
|
+
#
|
5
|
+
# author: jhw@prosperworks.com
|
6
|
+
# incept: 2016-03-05
|
7
|
+
#
|
2
8
|
|
3
|
-
|
4
|
-
require
|
9
|
+
require_relative '../lib/oak.rb'
|
10
|
+
require 'optimist'
|
5
11
|
|
6
|
-
|
12
|
+
OLD_ARGV = ARGV.dup # ARGV is consumed by Optimist but we use later.
|
13
|
+
OPTS = Optimist.options do
|
14
|
+
banner "#{$0} cli driver for OAK"
|
15
|
+
banner <<-OPTIMIST_EXAMPLES
|
16
|
+
Examples:
|
17
|
+
$ echo hello | bin/oak.rb
|
18
|
+
oak_3CNB_1944283675_15_RjFTVTVfaGVsbG8_ok
|
19
|
+
$ (echo hello ; echo world) | bin/oak.rb
|
20
|
+
oak_3CNB_1944283675_15_RjFTVTVfaGVsbG8_ok
|
21
|
+
oak_3CNB_2139413982_15_RjFTVTVfd29ybGQ_ok
|
22
|
+
$ (echo hello ; echo world) | bin/oak.rb --compression zlib --force
|
23
|
+
oak_3CZB_1944283675_26_eJxzMwwONY3PSM3JyQcAFF4DyA_ok
|
24
|
+
oak_3CZB_2139413982_26_eJxzMwwONY0vzy_KSQEAFNgD3A_ok
|
25
|
+
$ (echo hello ; echo world) | bin/oak.rb --format none
|
26
|
+
oak_3CNN_1944283675_11_F1SU5_hello_ok
|
27
|
+
oak_3CNN_2139413982_11_F1SU5_world_ok
|
28
|
+
$ (echo hello ; echo world) | bin/oak.rb | bin/oak.rb --mode decode-lines
|
29
|
+
hello
|
30
|
+
world
|
31
|
+
OPTIMIST_EXAMPLES
|
32
|
+
banner "Options:"
|
33
|
+
version "#{$0} #{OAK::VERSION}"
|
34
|
+
opt :redundancy, 'redundancy', :default => 'crc32'
|
35
|
+
opt :format, 'format', :default => 'base64'
|
36
|
+
opt :compression, 'compression', :default => 'none'
|
37
|
+
opt :force, 'compress even if bigger', :default => false
|
38
|
+
opt :mode, 'mode', :default => 'encode-lines'
|
39
|
+
opt :key_chain, 'key chain env name', :type => :string
|
40
|
+
opt :key, 'encrypt key name', :type => :string
|
41
|
+
opt :key_check, 'check available keys', :default => false
|
42
|
+
opt :key_generate, 'generate new key', :default => false
|
43
|
+
opt :force_oak_4, 'force OAK_4 even unencrypted', :default => false
|
44
|
+
opt :eigen, 'calc eigenratio', :type => :int
|
45
|
+
opt :self_test, 'self-test only', :default => false
|
46
|
+
opt :help, 'show this help'
|
47
|
+
end
|
48
|
+
Optimist::die :eigen, "must be non-negative" if OPTS[:eigen] && OPTS[:eigen] < 0
|
49
|
+
|
50
|
+
oak_opts = {}
|
51
|
+
oak_opts[:redundancy] = OPTS[:redundancy]
|
52
|
+
oak_opts[:compression] = OPTS[:compression]
|
53
|
+
oak_opts[:force] = OPTS[:force]
|
54
|
+
oak_opts[:format] = OPTS[:format]
|
55
|
+
oak_opts[:key_chain] = OAK.parse_env_chain(ENV,OPTS[:key_chain])
|
56
|
+
oak_opts[:key] = OPTS[:key]
|
57
|
+
oak_opts[:force_oak_4] = OPTS[:force_oak_4]
|
58
|
+
|
59
|
+
if !OAK::REDUNDANCY_2_CODE.keys.include?(oak_opts[:redundancy])
|
60
|
+
Optimist::die :redundancy, "bogus #{OPTS[:redundancy]}"
|
61
|
+
end
|
62
|
+
if !OAK::COMPRESSION_2_CODE.keys.include?(oak_opts[:compression])
|
63
|
+
Optimist::die :compression, "bogus #{OPTS[:compression]}"
|
64
|
+
end
|
65
|
+
cool_formats = OAK::FORMAT_2_CODE.keys
|
66
|
+
if !cool_formats.include?(oak_opts[:format])
|
67
|
+
Optimist::die :format, "bogus #{OPTS[:format]} not in #{cool_formats}"
|
68
|
+
end
|
69
|
+
|
70
|
+
=begin
|
71
|
+
|
72
|
+
doctest: simple transcoding
|
73
|
+
>> OAK::decode(OAK::encode([1,"2",3.000001]))
|
74
|
+
=> [1,"2",3.000001]
|
75
|
+
>> OAK::decode(OAK::encode({foo: "bar"}))
|
76
|
+
=> {foo: "bar"}
|
77
|
+
>> OAK::decode(OAK::encode({foo: :bar}))
|
78
|
+
=> {foo: :bar}
|
79
|
+
>> OAK::decode(OAK::encode("Hello, World!"))
|
80
|
+
=> "Hello, World!"
|
81
|
+
>> OAK::decode(OAK::encode("Hello, World!", format: :none, redundancy: :none))
|
82
|
+
=> "Hello, World!"
|
83
|
+
|
84
|
+
doctest: stability of encoding
|
85
|
+
>> OAK::decode("oak_3NNB_0_30_RjNIMV8xXzJZQTNfZm9vU1UzX2Jhcg_ok")
|
86
|
+
=> {:foo=>"bar"}
|
87
|
+
>> OAK::encode(1, format: :base64, redundancy: :none)
|
88
|
+
=> "oak_3NNB_0_6_RjFJMQ_ok"
|
89
|
+
>> OAK::encode(1, format: :base64, redundancy: :crc32)
|
90
|
+
=> "oak_3CNB_3405226796_6_RjFJMQ_ok"
|
91
|
+
>> OAK::encode(1, format: :none, redundancy: :crc32)
|
92
|
+
=> "oak_3CNN_3405226796_4_F1I1_ok"
|
93
|
+
>> hello_utf8 = "Hello, World!".force_encoding('UTF-8')
|
94
|
+
=> "Hello, World!"
|
95
|
+
>> OAK::encode(hello_utf8, format: :base64, redundancy: :none)
|
96
|
+
=> "oak_3NNB_0_27_RjFTVTEzX0hlbGxvLCBXb3JsZCE_ok"
|
97
|
+
>> OAK::encode(hello_utf8, format: :none, redundancy: :crc32)
|
98
|
+
=> "oak_3CNN_2351984628_20_F1SU13_Hello, World!_ok"
|
99
|
+
|
100
|
+
Note above I used force_encoding('UTF-8') after discovering that with
|
101
|
+
Ruby 2.1.6 on Mac I get Encoding.default_encoding is UTF-8, but with
|
102
|
+
Ruby 2.1.6 on Linux I get Encoding.default_encoding is US-ASCII!
|
103
|
+
|
104
|
+
=end
|
105
|
+
|
106
|
+
if __FILE__ == $0
|
107
|
+
if OPTS[:self_test]
|
108
|
+
require 'rubydoctest'
|
109
|
+
exit RubyDocTest::Runner.new(File.read(__FILE__), __FILE__).run ? 0 : 1
|
110
|
+
end
|
111
|
+
if OPTS[:key_check]
|
112
|
+
if !OPTS[:key_chain]
|
113
|
+
puts "no --key-chain specified"
|
114
|
+
else
|
115
|
+
keys = oak_opts[:key_chain].keys.keys
|
116
|
+
if 0 == keys.size
|
117
|
+
puts "#{OPTS[:key_chain]}: no keys found"
|
118
|
+
else
|
119
|
+
puts "#{OPTS[:key_chain]}: found keys: #{keys.join(' ')}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
if OPTS[:key_generate]
|
124
|
+
STDOUT.puts OAK.encode(OAK.random_key)
|
125
|
+
exit 0
|
126
|
+
end
|
127
|
+
if !$stdin.tty?
|
128
|
+
if OPTS[:eigen]
|
129
|
+
prev = STDIN.read
|
130
|
+
puts "input: %d" % prev.size
|
131
|
+
OPTS[:eigen].times do |i|
|
132
|
+
oak = OAK.encode(prev,oak_opts)
|
133
|
+
psize = prev.size
|
134
|
+
wsize = oak.size
|
135
|
+
ratio = 1.0 * wsize / psize
|
136
|
+
puts " iter %3d: %4d => %4d ratio %.2f" % [i,psize,wsize,ratio]
|
137
|
+
prev = oak
|
138
|
+
end
|
139
|
+
exit 0
|
140
|
+
end
|
141
|
+
unhappiness = 0
|
142
|
+
case OPTS[:mode]
|
143
|
+
when 'cat'
|
144
|
+
ARGF.each_line.map(&:strip).each do |line|
|
145
|
+
puts line
|
146
|
+
end
|
147
|
+
when 'encode-lines'
|
148
|
+
ARGF.each_line.map(&:strip).each do |line|
|
149
|
+
puts OAK.encode(line,oak_opts)
|
150
|
+
end
|
151
|
+
when 'decode-lines'
|
152
|
+
ARGF.each_line.map(&:strip).each do |line|
|
153
|
+
puts OAK.decode(line,oak_opts)
|
154
|
+
end
|
155
|
+
when 'encode-file'
|
156
|
+
puts OAK.encode(STDIN.read,oak_opts)
|
157
|
+
when 'decode-file'
|
158
|
+
STDOUT.write OAK.decode(STDIN.read.strip,oak_opts)
|
159
|
+
when 'recode-file'
|
160
|
+
puts OAK.encode(OAK.decode(STDIN.read,oak_opts),oak_opts)
|
161
|
+
when 'crazy'
|
162
|
+
#
|
163
|
+
# --mode crazy prints out a sample of OAK strings for various
|
164
|
+
# challenging cases.
|
165
|
+
#
|
166
|
+
cycle_a = ['cycle_a','TBD']
|
167
|
+
cycle_b = ['cycle_b',cycle_a]
|
168
|
+
cycle_a[1] = cycle_b
|
169
|
+
dag_c = ['dag_c']
|
170
|
+
dag_b = ['dag_b',dag_c]
|
171
|
+
dag_a = ['dag_a',dag_b,dag_c]
|
172
|
+
[
|
173
|
+
'hello',
|
174
|
+
['hello'] + ['hello',:hello] * 2,
|
175
|
+
{1=>'a','b'=>2,[]=>3,''=>4,{}=>5,nil=>6},
|
176
|
+
['x','x','x','x','x','x','x','x','x','x','x','x','x'],
|
177
|
+
['x'] * 13,
|
178
|
+
cycle_a,
|
179
|
+
dag_a,
|
180
|
+
[1,-123,0.12,-0.123,Float::NAN,-Float::INFINITY,3.14159265358979],
|
181
|
+
].each do |obj|
|
182
|
+
oak = OAK.encode(
|
183
|
+
obj,
|
184
|
+
redundancy: :crc32,
|
185
|
+
format: :none,
|
186
|
+
compression: :none,
|
187
|
+
)
|
188
|
+
puts ""
|
189
|
+
puts "obj: #{obj}"
|
190
|
+
puts " oak: #{oak}"
|
191
|
+
begin
|
192
|
+
dec = OAK.decode(oak,oak_opts)
|
193
|
+
if dec != obj
|
194
|
+
if !dec.is_a?(Float) && !enc.is_a?(Float) && !dec.nan? && !enc.nan?
|
195
|
+
unhappiness += 1
|
196
|
+
puts " BAD: #{dec}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
rescue OAK::CantTouchThisStringError => ex
|
200
|
+
puts " BAD: #{ex.message}: #{ex.backtrace_locations[0]}"
|
201
|
+
unhappiness += 1
|
202
|
+
end
|
203
|
+
end
|
204
|
+
when 'tests'
|
205
|
+
[
|
206
|
+
[1,2,3],
|
207
|
+
{:foo=>'foo','foo'=>['x']*10},
|
208
|
+
-1,
|
209
|
+
Float::NAN,
|
210
|
+
nil,
|
211
|
+
].each do |obj|
|
212
|
+
puts " #{obj} => ["
|
213
|
+
key_chain = OAK::KeyChain.new(
|
214
|
+
{ 'l0ng3r' => OAK::Key.new('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') }
|
215
|
+
)
|
216
|
+
[
|
217
|
+
{redundancy: :none, format: :none, compression: :none },
|
218
|
+
{redundancy: :none, format: :base64,compression: :lz4, force: true},
|
219
|
+
{redundancy: :crc32,format: :base64,compression: :zlib, force: true},
|
220
|
+
{redundancy: :crc32,format: :base64,compression: :bzip2,force: true},
|
221
|
+
{redundancy: :sha1, format: :base64,compression: :lzma, force: true},
|
222
|
+
{key_chain: key_chain,force_oak_4: true,format: :none, },
|
223
|
+
{key_chain: key_chain,force_oak_4: true, },
|
224
|
+
{key_chain: key_chain,key: 'l0ng3r', },
|
225
|
+
].each do |opts|
|
226
|
+
oak = OAK.encode(obj,opts)
|
227
|
+
puts " '#{oak}',"
|
228
|
+
dec = OAK.decode(oak,opts)
|
229
|
+
if dec != obj
|
230
|
+
if !dec.is_a?(Float) && !enc.is_a?(Float) && !dec.nan? && !enc.nan?
|
231
|
+
unhappiness += 1
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
puts " ],"
|
236
|
+
end
|
237
|
+
else
|
238
|
+
Optimist::die :mode, "bogus mode #{OPTS[:mode]}"
|
239
|
+
end
|
240
|
+
if unhappiness > 0
|
241
|
+
puts "unhappiness: #{unhappiness}"
|
242
|
+
end
|
243
|
+
exit unhappiness
|
244
|
+
end
|
245
|
+
end
|