sixword 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b74ea6c562414ef7a07f742098179f75bccb9807cd5e75161d5f8cb2a04ee780
4
- data.tar.gz: 9e5842fc333709e4469d518d5fcffd90987a60fff2c070892923e69dcff19c93
3
+ metadata.gz: feb344c9058b513d794ccab85edbd778cce35d3f858ae7a980ad40c8cebc9ec2
4
+ data.tar.gz: 49726f7f0ea5f242a3a4091ab56553147ad96d5ed110b8283b10b6cef7f39644
5
5
  SHA512:
6
- metadata.gz: 8b238947058e4b05daadb950f3c67f72c7ace5982d97b7e7ec68ad5047ec93ccf3b82af3b0cd9956296e04b1f625a6c0d755db6f3443612ba947a5cf68a16a9d
7
- data.tar.gz: 05d8e291207ad2fcc499584a7647de32ef7f9c4a41a2d9d6b35c0a8629de7eb552e880e938a3990fa4ea2a52f8b22b618372b1bc05c2981104027da028d0eb27
6
+ metadata.gz: f5a60cc54e7c31beefa84c8f4a9532db95f397770109cffad1e7b314f38758505915cfbf13119094357c4b9bc4d5def43b36a67eaf4332a70a19b2ba66b2231f
7
+ data.tar.gz: f5991b440d27855a484630a35638203dae59f1ca4c26868316cf7a8b9c9588837522b40701483c11721c4e9bc45ea712abea08bd6c6cd4c9556f333bae3871c5
@@ -7,10 +7,10 @@ jobs:
7
7
  matrix:
8
8
  os: [ubuntu-latest] #, macos-latest
9
9
  # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
10
- ruby: [2.6, 2.7, '3.0', head, jruby, jruby-head]
10
+ ruby: ['3.0', 3.1, 3.2, 3.3, 3.4, head, jruby, jruby-head]
11
11
  runs-on: ${{ matrix.os }}
12
12
  steps:
13
- - uses: actions/checkout@v2
13
+ - uses: actions/checkout@v5
14
14
  - uses: ruby/setup-ruby@v1
15
15
  with:
16
16
  ruby-version: ${{ matrix.ruby }}
data/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ This project adheres to [Semantic Versioning](http://semver.org).
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.5.0] -- 2025-09-17
8
+
9
+ - Require ruby version >= 3.0.
10
+ - Update dependencies and test on newer ruby versions.
11
+ - Add a base64 encoding/decoding mode.
12
+ - Fix a minor bug with hex style colons.
13
+ - Refactor help text to be easier to follow.
14
+
15
+
7
16
  ## [0.3.5] -- 2018-03-28
8
17
 
9
18
  - Fix up rubocop config etc.
data/bin/sixword CHANGED
@@ -17,50 +17,41 @@ usage: #{BaseName} [OPTION]... [FILE]
17
17
 
18
18
  Six-word encode or decode FILE, or standard input, to standard output.
19
19
 
20
- The data are encoded using the 2048 word dictionary created for S/Key (tm) and
21
- standardized by RFC 2289, RFC 1760, and RFC 1751. It also supports an optional
22
- custom padding scheme to allow for messages that are not a multiple of 8 bytes.
20
+ Binary encoding for humans
23
21
 
24
- With no FILE, or when FILE is -, read standard input.
25
-
26
- #{BaseName} understands a few different styles of hexadecimal input and output.
27
- When encoding, the input will first be decoded to binary data before being
28
- encoded to words. When decoding, the output will be encoded in hex.
29
-
30
-
31
- Hex styles:
22
+ #{BaseName} is similar to base64, but intended for humans to read and type.
23
+ The six-word encoding uses a dictionary of 2048 short English words.
24
+ Each 8-byte input becomes 6 words of output, including 2 checksum bits.
25
+ This makes it easy to read and type quickly with built-in error checking.
32
26
 
33
- lower/lowercase:
34
- Accept a variety of inputs, but don't include whitespace in output.
27
+ Use cases include sharing cryptographic keys on paper, verifying fingerprints,
28
+ or comparing binary data.
35
29
 
36
- Encoding:
37
- '48:69:20:77:6f:72:6c:64' => 'Hi world' => 'ACRE ADEN INN SLID MAD PAP'
38
- Decoding:
39
- 'ACRE ADEN INN SLID MAD PAP' => 'Hi world' => '486920776f726c64'
40
-
41
- finger/fingerprint:
42
- Accept a variety of inputs, and include whitespace in output.
43
- This is intended to look something like GPG fingerprints.
44
-
45
- Encoding:
46
- '4869 2077 6F72 6C64' => 'Hi world' => 'ACRE ADEN INN SLID MAP PAP'
47
- Decoding:
48
- 'ACRE ADEN INN SLID MAP PAP' => 'Hi world' => '4869 2077 6F72 6C64'
30
+ The six-word encoding was created for S/Key (tm) and standardized by RFC 2289,
31
+ RFC 1760, and RFC 1751. This #{BaseName} implementation also supports an
32
+ optional non-standard padding scheme to allow for messages that are not a
33
+ multiple of 8 bytes.
49
34
 
35
+ With no FILE, or when FILE is -, read standard input.
50
36
 
51
37
  Options:
52
38
  EOM
53
39
 
54
- opts.on('-h', '--help', 'Display this message', ' ') do
40
+ opts.on('-h', '--help', 'Display this message') do
55
41
  $stderr.puts opts, ''
56
42
  exit 0
57
43
  end
58
- opts.on('-v', '--version', 'Print version number', ' ') do
44
+
45
+ # --
46
+ opts.separator('')
47
+
48
+ opts.on('-v', '--version', 'Print version number') do
59
49
  puts 'sixword ' + Sixword::VERSION
60
50
  exit 0
61
51
  end
62
52
 
63
- #
53
+ # --
54
+ opts.separator('')
64
55
 
65
56
  opts.on('-d', '--decode', 'Decode data') do
66
57
  options[:mode] = :decode
@@ -70,32 +61,137 @@ Options:
70
61
  options[:mode] = :encode
71
62
  end
72
63
 
73
- opts.on('-p', '--no-pad', "Don't use custom padding scheme", ' ') do
64
+ opts.on('-p', '--no-pad', "Don't use custom padding scheme") do
74
65
  options[:pad] = false
75
66
  end
76
67
 
77
- #
68
+ # --
69
+ opts.separator('')
70
+
71
+ opts.on('-S', '--style STYLE',
72
+ 'Use STYLE as format/coding for plain text') do |style|
73
+ options[:style] = style
74
+ end
78
75
 
79
- opts.on('-S', '--hex-style STYLE',
80
- 'Treat input (when encoding) or print',
81
- 'output (when decoding) as hex') do |style|
82
- options[:hex_style] = style
76
+ opts.on('--hex-style STYLE', 'Alias for --style') do |style|
77
+ options[:style] = style
78
+ end
79
+
80
+ opts.on('-B', '--base64', 'Use base64 input/output') do
81
+ options[:style] = 'base64'
83
82
  end
84
83
 
85
84
  opts.on('-H', '--hex', 'Short for --hex-style lowercase') do
86
- options[:hex_style] = 'lowercase'
85
+ options[:style] = 'lowercase'
87
86
  end
88
87
 
89
- opts.on('-f', '--fingerprint', 'Short for --hex-style fingerprint', ' ') do
90
- options[:hex_style] = 'fingerprint'
88
+ opts.on('-f', '--fingerprint', 'Short for --hex-style fingerprint') do
89
+ options[:style] = 'fingerprint'
91
90
  end
92
91
 
93
- #
92
+ # --
93
+ opts.separator('')
94
94
 
95
95
  opts.on('-w', '--line-width NUM',
96
96
  'Print NUM sentences per line when encoding') do |arg|
97
97
  options[:line_width] = Integer(arg)
98
98
  end
99
+
100
+ opts.separator('')
101
+
102
+
103
+ opts.separator <<-EOM
104
+ Encoding Styles:
105
+
106
+ #{BaseName} understands a few different styles of hexadecimal input and output.
107
+ When encoding, the input will first be decoded to binary data before being
108
+ encoded to words. When decoding, the output will be encoded in hex.
109
+
110
+ The --style STYLE option can be used to encode to / decode from formats other
111
+ than raw binary bytes. This is useful when interacting with binary data like a
112
+ key that is already encoded in hex or base64.
113
+
114
+ For example, with --style base64, input for encode is assumed to be base64, and
115
+ output from decode will be base64.
116
+
117
+ This table shows how the 8 bytes "Hi world" would be encoded in each style.
118
+ (Sixword: "ACRE ADEN INN SLID MAD PAP")
119
+
120
+ ╭─────────────────────┬─────────────────────────╮
121
+ │ Style │ Example input/output │
122
+ ├─────────────────────┼─────────────────────────┤
123
+ │ <default> │ Hi world │
124
+ │ base64 │ SGkgd29ybGQ= │
125
+ │ lower, lowercase │ 486920776f726c64 │
126
+ │ finger, fingerprint │ 4869 2077 6F72 6C64 │
127
+ │ colons │ 48:69:20:77:6f:72:6c:64 │
128
+ ╰─────────────────────┴─────────────────────────╯
129
+
130
+ The hex styles all accept hexadecimal input in a variety of formats.
131
+ They allow `a-fA-F0-9` and ignore whitespace and certain punctuation: `:.-`
132
+ (colon, period, hyphen).
133
+
134
+ Examples:
135
+
136
+ Normal encoding and decoding:
137
+
138
+ $ echo 'Testing' | sixword
139
+ BEAK NET SITE ROTH SWIM FORM
140
+
141
+ $ sixword -d <<< 'BEAK NET SITE ROTH SWIM FORM'
142
+ Testing
143
+
144
+ $ sixword -d <<< 'beak net site roth swim form'
145
+ Testing
146
+
147
+
148
+ Random 128-bit (16 byte) key:
149
+
150
+ $ head -c 16 /dev/urandom | sixword
151
+ THIS IOTA DOUG FELT OIL MITT
152
+ LIED LAIN AND HULK MADE LAUD
153
+
154
+ $ cat > key.txt
155
+ this iota doug felt oil mitt
156
+ lied lain and hulk made laud
157
+
158
+ $ sixword -d key.txt | xxd
159
+ 00000000: eab3 ddd7 bf92 f179 ae35 3008 ce4b 5b56 .......y.50..K[V
160
+
161
+
162
+ With encoding styles:
163
+
164
+ $ sixword -H <<< '54:65:73:74:69:6e:67:0a'
165
+ BEAK NET SITE ROTH SWIM FORM
166
+
167
+ $ sixword -dH <<< 'BEAK NET SITE ROTH SWIM FORM'
168
+ 54657374696e670a
169
+
170
+ $ sixword -df <<< 'BEAK NET SITE ROTH SWIM FORM'
171
+ 5465 7374 696E 670A
172
+
173
+ $ sixword -d --style colons <<< 'BEAK NET SITE ROTH SWIM FORM'
174
+ 54:65:73:74:69:6e:67:0a
175
+
176
+ $ sixword -d --style base64 <<< 'BEAK NET SITE ROTH SWIM FORM'
177
+ VGVzdGluZwo=
178
+
179
+ $ sixword -d <<< 'BEAK NET SITE ROTH SWIM FORM' | base64
180
+ VGVzdGluZwo=
181
+
182
+
183
+ Errors:
184
+
185
+ $ sixword -d <<< 'BEAK NET SITE ROTH SWIM FOR'
186
+ sixword: Parity bits do not match
187
+ [exit status 3]
188
+
189
+ $ echo 'abc' | sixword --no-pad
190
+ sixword: Must pad bytes to multiple of 8 or use pad_encode
191
+ [exit status 10]
192
+
193
+ EOM
194
+
99
195
  end
100
196
 
101
197
  begin
data/lib/sixword/cli.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'base64'
2
+
1
3
  module Sixword
2
4
 
3
5
  # The Sixword::CLI class implements all of the complex processing needed for
@@ -26,7 +28,8 @@ module Sixword
26
28
  #
27
29
  # @option options [:encode, :decode] :mode (:encode)
28
30
  # @option options [Boolean] :pad (false)
29
- # @option options [String] :hex_style
31
+ # @option options [String] :style
32
+ # @option options [String] :hex_style Alias of :style
30
33
  # @option options [Integer] :line_width (1) In encode mode, the number of
31
34
  # sentences to output per line.
32
35
  #
@@ -62,15 +65,25 @@ module Sixword
62
65
  mode == :encode
63
66
  end
64
67
 
68
+ # Return the value of the :style option.
69
+ # @return [String, nil]
70
+ def style
71
+ options[:style] || options[:hex_style]
72
+ end
73
+
65
74
  # Return the value of the :hex_style option.
75
+ # @deprecated Use {#style} instead.
66
76
  # @return [String, nil]
67
77
  def hex_style
68
- options[:hex_style]
78
+ options[:style] || options[:hex_style]
69
79
  end
70
80
 
71
81
  # Format data as hex in various styles.
72
82
  def print_hex(data, chunk_index, cols=80)
73
83
  case hex_style
84
+ when 'b64', 'base64'
85
+ # encode as base64
86
+ print Base64.strict_encode64(data)
74
87
  when 'lower', 'lowercase'
75
88
  # encode to lowercase hex with no newlines
76
89
  print Sixword::Hex.encode(data)
@@ -121,8 +134,15 @@ module Sixword
121
134
  raise ArgumentError.new("block is required")
122
135
  end
123
136
 
124
- read_input_by_6_words do |arr|
125
- yield Sixword.decode(arr, padding_ok: pad?)
137
+ # base64 decoding must happen in one shot to avoid inserting unnecessary
138
+ # padding in the middle
139
+ if hex_style == 'base64' || hex_style == 'b64'
140
+ yield Sixword.decode(stream.read, padding_ok: pad?)
141
+ else
142
+ # chunk input in 6-word sentences
143
+ read_input_by_6_words do |arr|
144
+ yield Sixword.decode(arr, padding_ok: pad?)
145
+ end
126
146
  end
127
147
  end
128
148
 
@@ -157,7 +177,16 @@ module Sixword
157
177
  raise ArgumentError.new("block is required")
158
178
  end
159
179
 
160
- if hex_style
180
+ if hex_style == "base64" || hex_style == "b64"
181
+ # Base64 needs 10.66 bytes to encode 8 binary bytes
182
+ # so we can't neatly chunk input like we can with hex.
183
+ # Instead just read the whole thing and yield in chunks of 8 bytes.
184
+ buf = Base64.decode64(stream.read)
185
+ buf.each_char.each_slice(8) do |chunk|
186
+ yield chunk.join
187
+ end
188
+
189
+ elsif hex_style
161
190
  # yield data in chunks from accumulate_hex_input until EOF
162
191
  accumulate_hex_input do |hex|
163
192
  begin
@@ -169,6 +198,7 @@ module Sixword
169
198
  yield data
170
199
  end
171
200
  end
201
+
172
202
  else
173
203
  # yield data 8 bytes at a time until EOF
174
204
  while true
@@ -219,8 +249,9 @@ module Sixword
219
249
  case hex_style
220
250
  when 'lower', 'lowercase'
221
251
  when 'finger', 'fingerprint'
252
+ when 'colon', 'colons'
222
253
  else
223
- raise CLIError.new("unknown hex style: #{hex_style.inspect}")
254
+ raise CLIError.new("unknown style: #{hex_style.inspect}")
224
255
  end
225
256
 
226
257
  while true
@@ -1,4 +1,4 @@
1
1
  module Sixword
2
2
  # version string
3
- VERSION = '0.4.0'.freeze
3
+ VERSION = '0.5.0'.freeze
4
4
  end
data/sixword.gemspec CHANGED
@@ -8,13 +8,14 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Sixword::VERSION
9
9
  spec.authors = ['Andy Brody']
10
10
  spec.email = ['git@abrody.com']
11
- spec.summary = 'Implementation of RFC 2289 compatible 6-word encoding'
11
+ spec.summary = 'Encode binary data for humans (RFC 2289 compatible 6-word encoding)'
12
12
  spec.description = <<-EOM
13
- Sixword implements the 6-word binary encoding created for S/Key (tm) and
14
- standardized by RFC 2289, RFC 1760, and RFC 1751. Binary data may be
15
- encoded using a dictionary of 2048 English words of 1-4 characters in
16
- length. Each block of 64 bits is encoded using 6 words, which includes 2
17
- parity bits. It is ideal for transmitting binary data such as cryptographic
13
+ Sixword encodes binary data in a human-friendly format using English words.
14
+ It uses the 6-word binary encoding created for S/Key (tm) and standardized
15
+ by RFC 2289, RFC 1760, and RFC 1751. Binary data is encoded using a
16
+ dictionary of 2048 short English words (1-4 letters in length). Each block
17
+ of 64 bits is encoded using 6 words, which includes 2 parity bits for error
18
+ checking. This is ideal for transmitting binary data such as cryptographic
18
19
  keys where humans must communicate or enter the values.
19
20
 
20
21
  See also: Bubble Babble, PGP Word List, Diceware, Base64, Base32
@@ -34,5 +35,7 @@ Gem::Specification.new do |spec|
34
35
  spec.add_development_dependency 'rubocop', '~> 0.49'
35
36
  spec.add_development_dependency 'yard'
36
37
 
37
- spec.required_ruby_version = '>= 1.9.3'
38
+ spec.add_dependency 'base64'
39
+
40
+ spec.required_ruby_version = '>= 3.0'
38
41
  end
data/spec/cli/cli_spec.rb CHANGED
@@ -80,7 +80,7 @@ RSpec.describe Sixword::CLI do
80
80
  end
81
81
 
82
82
  it 'returns expected error codes in various conditions' do
83
- run_sixword(%w{-e --hex-style nonexistent}, '', /unknown hex style/, 2)
83
+ run_sixword(%w{-e --hex-style nonexistent}, '', /unknown style/, 2)
84
84
  run_sixword(['-d'], "BEAK NET SITE ROTH SWIM FOR\n", /Parity bits do not match/, 3)
85
85
  run_sixword(['-d'], "ZZZ A A A A A\n", /Unknown word: "ZZZ"/, 4)
86
86
  run_sixword(['-d'], "AAAAAA A A A A A\n", /1-4 chars/, 5)
@@ -100,14 +100,35 @@ RSpec.describe Sixword::CLI do
100
100
 
101
101
  it 'handles basic hex styles' do
102
102
  run_sixword(['-H'], "54:65:73:74:69:6e:67:0a\n", "BEAK NET SITE ROTH SWIM FORM\n")
103
+ run_sixword(%w{-e -S colons}, "54:65:73:74:69:6e:67:0a\n", "BEAK NET SITE ROTH SWIM FORM\n")
103
104
  run_sixword(['-H'], "54657374696e670a\n", "BEAK NET SITE ROTH SWIM FORM\n")
104
105
  run_sixword(['-H'], "5465 7374 696E 670A\n", "BEAK NET SITE ROTH SWIM FORM\n")
106
+ run_sixword(%w{-S lower}, "54657374696e670a\n", "BEAK NET SITE ROTH SWIM FORM\n")
105
107
 
106
108
  run_sixword(['-dH'], "BEAK NET SITE ROTH SWIM FORM\n", "54657374696e670a\n")
107
109
  run_sixword(['-df'], "BEAK NET SITE ROTH SWIM FORM\n", "5465 7374 696E 670A\n")
108
110
  run_sixword(%w{-d -S colons}, "BEAK NET SITE ROTH SWIM FORM\n", "54:65:73:74:69:6e:67:0a\n")
109
111
  end
110
112
 
113
+ it 'handles base64' do
114
+ run_sixword(['--base64'], "VGVzdGluZwo=\n", "BEAK NET SITE ROTH SWIM FORM\n")
115
+ run_sixword(%w{-d --base64}, "BEAK NET SITE ROTH SWIM FORM\n", "VGVzdGluZwo=\n")
116
+ run_sixword(%w{-d -S base64}, "BEAK NET SITE ROTH SWIM FORM\n", "VGVzdGluZwo=\n")
117
+ end
118
+
119
+ it 'handles long base64' do
120
+ run_sixword(
121
+ %w{--base64},
122
+ "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1w",
123
+ "BEAK US ACHE SOUR BERN LOLA\nCORE ARC HULK SLID DREW DUE\nCHUB ENDS BOG RUSS BESS MAST\n"
124
+ )
125
+ run_sixword(
126
+ %w{-d --base64},
127
+ "BEAK US ACHE SOUR BERN LOLA\nCORE ARC HULK SLID DREW DUE\nCHUB ENDS BOG RUSS BESS MAST\n",
128
+ "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1w\n"
129
+ )
130
+ end
131
+
111
132
  it 'should encode/decode RFC hex vectors correctly' do
112
133
  Sixword::TestVectors::HexTests.each do |_section, tests|
113
134
  tests.each do |hex, sentence|
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sixword
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Brody
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2021-10-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -94,12 +93,27 @@ dependencies:
94
93
  - - ">="
95
94
  - !ruby/object:Gem::Version
96
95
  version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: base64
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
97
110
  description: |2
98
- Sixword implements the 6-word binary encoding created for S/Key (tm) and
99
- standardized by RFC 2289, RFC 1760, and RFC 1751. Binary data may be
100
- encoded using a dictionary of 2048 English words of 1-4 characters in
101
- length. Each block of 64 bits is encoded using 6 words, which includes 2
102
- parity bits. It is ideal for transmitting binary data such as cryptographic
111
+ Sixword encodes binary data in a human-friendly format using English words.
112
+ It uses the 6-word binary encoding created for S/Key (tm) and standardized
113
+ by RFC 2289, RFC 1760, and RFC 1751. Binary data is encoded using a
114
+ dictionary of 2048 short English words (1-4 letters in length). Each block
115
+ of 64 bits is encoded using 6 words, which includes 2 parity bits for error
116
+ checking. This is ideal for transmitting binary data such as cryptographic
103
117
  keys where humans must communicate or enter the values.
104
118
 
105
119
  See also: Bubble Babble, PGP Word List, Diceware, Base64, Base32
@@ -138,7 +152,6 @@ homepage: https://github.com/ab/sixword
138
152
  licenses:
139
153
  - GPL-3
140
154
  metadata: {}
141
- post_install_message:
142
155
  rdoc_options: []
143
156
  require_paths:
144
157
  - lib
@@ -146,17 +159,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
159
  requirements:
147
160
  - - ">="
148
161
  - !ruby/object:Gem::Version
149
- version: 1.9.3
162
+ version: '3.0'
150
163
  required_rubygems_version: !ruby/object:Gem::Requirement
151
164
  requirements:
152
165
  - - ">="
153
166
  - !ruby/object:Gem::Version
154
167
  version: '0'
155
168
  requirements: []
156
- rubygems_version: 3.0.3.1
157
- signing_key:
169
+ rubygems_version: 3.6.9
158
170
  specification_version: 4
159
- summary: Implementation of RFC 2289 compatible 6-word encoding
171
+ summary: Encode binary data for humans (RFC 2289 compatible 6-word encoding)
160
172
  test_files:
161
173
  - spec/cli/cli_spec.rb
162
174
  - spec/spec_helper.rb