kyle 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +18 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +21 -0
- data/README.md +244 -0
- data/bin/kyle +2 -2
- data/kyle.gemspec +22 -0
- data/lib/constants.rb +15 -0
- data/lib/kyle.rb +73 -232
- data/lib/kyle_commands.rb +159 -0
- data/spec/kyle_commands_spec.rb +33 -0
- data/spec/kyle_spec.rb +67 -0
- data/spec/spec_helper.rb +5 -0
- metadata +43 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f04555392f57d6f09a00cc5f1e2a5bc5f185efe
|
4
|
+
data.tar.gz: 5369fc8ad09357dd2791e3945c967f7f95290ae0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d2565eeb14b542d6afbbb0e940734329168172caa165535ac2679716a3d9fb9108b68af11ce49b89b9a92bc15ff38f860f4043895f898875ac0527d141e2799
|
7
|
+
data.tar.gz: af2beb34df3ae60876544336624936850695812c1f5a2b4d97e2ba3db9d46bb0d39ed3033f35c47b8316382936935e9deca50ad7a9031bcfd6f99b97414fab6d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
kyle (0.0.3)
|
5
|
+
highline (~> 1.6, >= 1.6.20)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.0.0)
|
11
|
+
astrolabe (1.3.0)
|
12
|
+
parser (>= 2.2.0.pre.3, < 3.0)
|
13
|
+
diff-lcs (1.2.5)
|
14
|
+
highline (1.6.21)
|
15
|
+
parser (2.2.0.pre.4)
|
16
|
+
ast (>= 1.1, < 3.0)
|
17
|
+
slop (~> 3.4, >= 3.4.5)
|
18
|
+
powerpack (0.0.9)
|
19
|
+
rainbow (2.0.0)
|
20
|
+
rspec (3.0.0)
|
21
|
+
rspec-core (~> 3.0.0)
|
22
|
+
rspec-expectations (~> 3.0.0)
|
23
|
+
rspec-mocks (~> 3.0.0)
|
24
|
+
rspec-core (3.0.3)
|
25
|
+
rspec-support (~> 3.0.0)
|
26
|
+
rspec-expectations (3.0.3)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.0.0)
|
29
|
+
rspec-mocks (3.0.3)
|
30
|
+
rspec-support (~> 3.0.0)
|
31
|
+
rspec-support (3.0.3)
|
32
|
+
rubocop (0.26.0)
|
33
|
+
astrolabe (~> 1.3)
|
34
|
+
parser (>= 2.2.0.pre.4, < 3.0)
|
35
|
+
powerpack (~> 0.0.6)
|
36
|
+
rainbow (>= 1.99.1, < 3.0)
|
37
|
+
ruby-progressbar (~> 1.4)
|
38
|
+
ruby-progressbar (1.5.1)
|
39
|
+
slop (3.6.0)
|
40
|
+
|
41
|
+
PLATFORMS
|
42
|
+
ruby
|
43
|
+
|
44
|
+
DEPENDENCIES
|
45
|
+
kyle!
|
46
|
+
rspec (~> 3.0.0)
|
47
|
+
rubocop (~> 0.26)
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Harun Esur
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
Kyle
|
2
|
+
====
|
3
|
+
A password manager for paranoids.
|
4
|
+
|
5
|
+
### Overview
|
6
|
+
|
7
|
+
Kyle differs from other password managers, since:
|
8
|
+
|
9
|
+
* It doesn't store any passwords so there is nothing to steal/crack for
|
10
|
+
attackers.
|
11
|
+
* However, you can't store any given password, but must set one generated by
|
12
|
+
Kyle.
|
13
|
+
|
14
|
+
Kyle differs from other password generators, since:
|
15
|
+
|
16
|
+
* Generated passwords are not random, but a brute-force method can take
|
17
|
+
thousands of years to crack one:
|
18
|
+
|
19
|
+
e.g. on the test vectors Bill Gates' password tooks 12.11 seconds on a MacBook
|
20
|
+
Pro Early 2013 with 2,4 GHZ Intel Core i7. So even for a lazy master-key with
|
21
|
+
8 chars includes small-case-letters and numbers, there are
|
22
|
+
`36^8+36^7+36^5+36^4+36^3+36^2+36 = 2901713047668` combinations; with 12.11s
|
23
|
+
per combination, it would take ***1,114,274 years*** to try all combinations.
|
24
|
+
|
25
|
+
* It doesn't use any specific hash or encryption algorithm, using a mixture of
|
26
|
+
several, chosen using the input info and key.
|
27
|
+
|
28
|
+
### Installation
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ gem install kyle
|
32
|
+
```
|
33
|
+
|
34
|
+
### Usage
|
35
|
+
|
36
|
+
Just type `kyle` on the command line to run, and pick any password depending
|
37
|
+
on your favourite animal.
|
38
|
+
|
39
|
+
```bash
|
40
|
+
$ kyle
|
41
|
+
Hostname:
|
42
|
+
abc.com
|
43
|
+
Account:
|
44
|
+
superuser
|
45
|
+
Port:
|
46
|
+
80
|
47
|
+
Key:
|
48
|
+
|
49
|
+
|
50
|
+
Ape _,o_iMmO5L!ZRlQH
|
51
|
+
Bat EZPBcTf6oo-jzWpM
|
52
|
+
Bear .ZmYlZ4PQpdOfish
|
53
|
+
Whale wb%EOphi7uqySwRZ
|
54
|
+
Crow eTXvLc.4FgTdIEJ%
|
55
|
+
Dog .Q,PBaMeFRO8nG-a
|
56
|
+
Cat ,lHFEMVXo%SjTlsm
|
57
|
+
Wasp e0CyUAHvs9-ljGFr
|
58
|
+
Fox 2%yxWtBZz-cOVW@b
|
59
|
+
Gull avuR86nGjG6DNkkX
|
60
|
+
Jackal +zhRwHPWCHknxlZp
|
61
|
+
Lion xMxPwb0E+5vQ_q4x
|
62
|
+
Panda qj7GQqJP7EKjU*gG
|
63
|
+
Rat kvniGIszq758@Sie
|
64
|
+
Shark 1aF3.iiV,e*OTGpT
|
65
|
+
Spider *nrvUtila0wnmb22
|
66
|
+
Turtle wYQerXRYffJJGvxZ
|
67
|
+
Wolf MD!VTDkikYxZvzM!
|
68
|
+
Zebra asVw!Q/5!QvqxiRf
|
69
|
+
```
|
70
|
+
|
71
|
+
You can also specify the hostname, account, port, and animal when typing the
|
72
|
+
command:
|
73
|
+
|
74
|
+
```bash
|
75
|
+
$ kyle abc.com superuser 80 jackal
|
76
|
+
Key:
|
77
|
+
|
78
|
+
|
79
|
+
+zhRwHPWCHknxlZp
|
80
|
+
```
|
81
|
+
|
82
|
+
Arguments must in this order, but any missing ones will be prompted for.
|
83
|
+
|
84
|
+
Adding the `-c` flag will prompt for the key twice, so you can be sure you
|
85
|
+
didn't make a typo.
|
86
|
+
|
87
|
+
Adding the `-r` flag saves the hostname/account/port combination in `~/.kyle`.
|
88
|
+
|
89
|
+
Adding `-a ` flag lets you choose from one of savedhost/account/port records saved with -r;
|
90
|
+
|
91
|
+
|
92
|
+
#### Batch usage
|
93
|
+
|
94
|
+
```bash
|
95
|
+
$ kyle -b path/to/file.kyle animal
|
96
|
+
Key:
|
97
|
+
|
98
|
+
|
99
|
+
hostname:account:port (animal) = password
|
100
|
+
hostname:account:port (animal) = password
|
101
|
+
```
|
102
|
+
|
103
|
+
Where `file.kyle` contains triples of hostname, account, port separated by
|
104
|
+
semi-colons (`;`), one per line. E.g.:
|
105
|
+
|
106
|
+
```
|
107
|
+
facebook.com;zuckerberg;80
|
108
|
+
amazon.com;bezos;443
|
109
|
+
```
|
110
|
+
|
111
|
+
### Changelog
|
112
|
+
|
113
|
+
#### 0.0.5
|
114
|
+
|
115
|
+
* Merge version with Isaac Seymour's efforts to unify multiple development lines that includes;
|
116
|
+
|
117
|
+
* Refactored code to have proper Rspec tests, and respect Rubocop conventions
|
118
|
+
|
119
|
+
* Added ability to specify hostname, account, port, and animal as args to the
|
120
|
+
executable
|
121
|
+
|
122
|
+
#### 0.0.4
|
123
|
+
|
124
|
+
* Added -a (Auto) flag
|
125
|
+
|
126
|
+
#### 0.0.2
|
127
|
+
|
128
|
+
* Added -b (BATCH) mode which help you generate bulk passwords;
|
129
|
+
|
130
|
+
* Added -r option to add entered values to <USER_HOME>/.kyle file
|
131
|
+
|
132
|
+
### Algorithm
|
133
|
+
|
134
|
+
#### Overall
|
135
|
+
|
136
|
+
```
|
137
|
+
|
138
|
+
+--------+ +-------+ +----+ +----------+
|
139
|
+
|HOSTNAME| |ACCOUNT| |PORT| |MASTER-KEY|
|
140
|
+
+---+----+ +---+---+ +--+-+ +-----+----+
|
141
|
+
| | | |
|
142
|
+
v v v v
|
143
|
+
+------+ +------+ +------+ +------+
|
144
|
+
|I.HASH| |I.HASH| |I.HASH| |I.HASH|
|
145
|
+
+------+ +------+ +------+ +------+
|
146
|
+
| | | |
|
147
|
+
v v v v
|
148
|
+
+------+ +------+ +------+ +------+
|
149
|
+
|I.HASH| |I.HASH| |I.HASH| |I.HASH|
|
150
|
+
+------+ +------+ +------+ +------+
|
151
|
+
+ + + +
|
152
|
+
| | | |
|
153
|
+
+-----+++----+ +-----+++----+
|
154
|
+
| |
|
155
|
+
v v
|
156
|
+
+------+ +------+
|
157
|
+
|I.ENC.| |I.ENC.|
|
158
|
+
+------+ +------+
|
159
|
+
+ +
|
160
|
+
| |
|
161
|
+
+------------+++-------+
|
162
|
+
|
|
163
|
+
v
|
164
|
+
+------+
|
165
|
+
|I.ENC.|
|
166
|
+
+------+ +------------+
|
167
|
+
+ |ANIMAL NAMES|
|
168
|
+
| +------------+
|
169
|
+
ENC | A1..AN |
|
170
|
+
| +------------+
|
171
|
+
v
|
172
|
+
+--(A1..AN)------------------------------+
|
173
|
+
|RES = PBKDF2_HMAC_SHA1(ENC,RES,10000,32)|
|
174
|
+
+----------------------------------------+
|
175
|
+
+
|
176
|
+
v
|
177
|
+
+-----------------------------+
|
178
|
+
|HASH_TO_PASSWORD(SHA512(RES))|
|
179
|
+
+-----------------------------+
|
180
|
+
+ + +
|
181
|
+
| | |
|
182
|
+
(A1..AN)
|
183
|
+
| | |
|
184
|
+
v v v
|
185
|
+
|
186
|
+
MULTIPLE PASSES
|
187
|
+
|
188
|
+
```
|
189
|
+
|
190
|
+
#### Iterative Hash
|
191
|
+
|
192
|
+
```
|
193
|
+
+-------------+
|
194
|
+
Iterative Hash |ALGORITHMS |
|
195
|
+
+-------------+
|
196
|
+
+-------------+ +--> |SHA512 |
|
197
|
+
|Text=(t1..tn)| | +-------------+
|
198
|
+
+------+------+ +--> |SHA384 |
|
199
|
+
| | +-------------+
|
200
|
+
| +--> |SHA256 |
|
201
|
+
v | +-------------+
|
202
|
+
+--(i=1..n)-----------------------------------------+ +--> |SHA224 |
|
203
|
+
|HASH=ALGORITHM[(ti % ALGORITHMS.SIZE)](Text | HASH)|+-+ +-------------+
|
204
|
+
+---------------------------------------------------+ +--> |SHA1 |
|
205
|
+
+ | +-------------+
|
206
|
+
| +--> |SHA2 |
|
207
|
+
| | +-------------+
|
208
|
+
v +--> |MD5 |
|
209
|
+
| +-------------+
|
210
|
+
HASH +--> |MD4 |
|
211
|
+
| +-------------+
|
212
|
+
+--> |RIPEMD160 |
|
213
|
+
+-------------+
|
214
|
+
```
|
215
|
+
|
216
|
+
#### Iterative Encryption
|
217
|
+
|
218
|
+
```
|
219
|
+
+-------------+
|
220
|
+
Iterative Encryption |ALGORITHMS |
|
221
|
+
+-------------+
|
222
|
+
+-------------+ +--> |DES3 |
|
223
|
+
|TEXT=(t1..tn)| | +-------------+
|
224
|
+
|KEY=(k1..kn) | +--> |DESX |
|
225
|
+
+-------------+ | +-------------+
|
226
|
+
| +--> |DES |
|
227
|
+
v | +-------------+
|
228
|
+
+--(i=1..n)----------------------------------------------+ +--> |CAST |
|
229
|
+
|ENC.=ALGORITHM[(ti % ALGORITHMS.SIZE)]((Text | ENC.),KEY|--------->| +-------------+
|
230
|
+
+-------------------------------------------+------------+ +--> |BLOWFISH |
|
231
|
+
+ | ^ | +-------------+
|
232
|
+
| | | +--> |AES128 |
|
233
|
+
| | | | +-------------+
|
234
|
+
| v | +--> |AES192 |
|
235
|
+
| +--------------+------------+ | +-------------+
|
236
|
+
| |KEY=PBKDF2("kyle",10000,32)| +--> |AES256 |
|
237
|
+
| | IV=SHA512(KEY) | | +-------------+
|
238
|
+
| +---------------------------+ +--> |RC4 |
|
239
|
+
| +-------------+
|
240
|
+
v
|
241
|
+
|
242
|
+
ENCRYPTED
|
243
|
+
```
|
244
|
+
|
data/bin/kyle
CHANGED
data/kyle.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = 'kyle'
|
3
|
+
gem.version = '0.0.5'
|
4
|
+
gem.date = '2014-09-17'
|
5
|
+
|
6
|
+
gem.summary = 'Kyle'
|
7
|
+
gem.description = 'A password manager for paranoids.'
|
8
|
+
gem.authors = ['Harun Esur', 'Isaac Seymour']
|
9
|
+
gem.email = 'harun.esur@sceptive.com'
|
10
|
+
gem.homepage = 'http://sceptive.com'
|
11
|
+
gem.license = 'MIT'
|
12
|
+
gem.requirements << 'Ruby should be compiled with openssl support.'
|
13
|
+
|
14
|
+
gem.executables << 'kyle'
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split("\n")
|
17
|
+
|
18
|
+
gem.add_runtime_dependency 'highline', '~> 1.6', '>= 1.6.20'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rubocop', '~> 0.26'
|
21
|
+
gem.add_development_dependency 'rspec', '~> 3.0.0'
|
22
|
+
end
|
data/lib/constants.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# Algorithms, password characters, animals
|
2
|
+
module Constants
|
3
|
+
ENC_ALGS = %w(des3 desx des cast bf aes128 aes192 aes256 rc4).freeze
|
4
|
+
HASH_ALGS = %w(sha512 sha384 sha256 sha224 sha1 sha md5 md4 ripemd160).freeze
|
5
|
+
|
6
|
+
LETTERS = %w(q w e r t y u i o p a s d f g h j k l z x c v b n m).freeze
|
7
|
+
BIG_LETTERS = %w(Q W E R T Y U I O P A S D F G H J K L Z X C V B N M).freeze
|
8
|
+
NUMBERS = %w(0 1 2 3 4 5 6 7 8 9).freeze
|
9
|
+
SPECIAL_CHARS = %w(. @ + - * / % _ ! ,).freeze
|
10
|
+
|
11
|
+
PASSWORD_CHARS = (LETTERS + BIG_LETTERS + NUMBERS + SPECIAL_CHARS).freeze
|
12
|
+
|
13
|
+
ANIMALS = %w(Ape Bat Bear Whale Crow Dog Cat Wasp Fox Gull Jackal Lion Panda
|
14
|
+
Rat Shark Spider Turtle Wolf Zebra).freeze
|
15
|
+
end
|
data/lib/kyle.rb
CHANGED
@@ -1,277 +1,118 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
2
|
require 'openssl'
|
3
3
|
require 'highline/import'
|
4
|
+
require 'constants'
|
4
5
|
|
6
|
+
# Password generation from 4 inputs
|
7
|
+
class Kyle
|
8
|
+
attr_accessor :hostname, :account, :port, :key
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
def initialize(hostname, account, port, key)
|
11
|
+
@hostname = hostname
|
12
|
+
@account = account
|
13
|
+
@port = port
|
14
|
+
@key = key
|
15
|
+
end
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
]
|
17
|
+
def passwords
|
18
|
+
@passwords ||= generate
|
19
|
+
end
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
"a","s","d","f","g","h","j","k","l","z",
|
20
|
-
"x","c","v","b","n","m"
|
21
|
-
]
|
21
|
+
def generate
|
22
|
+
passwords = {}
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
"A","S","D","F","G","H","J","K","L","Z",
|
26
|
-
"X","C","V","B","N","M"
|
27
|
-
]
|
24
|
+
salt = encrypted_inputs
|
25
|
+
cipher = salt
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
]
|
27
|
+
Constants::ANIMALS.each do |a|
|
28
|
+
cipher = OpenSSL::PKCS5.pbkdf2_hmac_sha1(cipher, salt, 10_000, 32)
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
]
|
30
|
+
passwords[a] = Kyle.hash_to_password(cipher)
|
31
|
+
end
|
36
32
|
|
37
|
-
|
33
|
+
passwords
|
34
|
+
end
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
$schars.each { |c| $passctable << c }
|
36
|
+
def hashed_hostname
|
37
|
+
Kyle.hash_twice(hostname)
|
38
|
+
end
|
43
39
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
"Turtle", "Wolf", "Zebra"
|
48
|
-
]
|
40
|
+
def hashed_account
|
41
|
+
Kyle.hash_twice(account)
|
42
|
+
end
|
49
43
|
|
50
|
-
|
44
|
+
def hashed_port
|
45
|
+
Kyle.hash_twice(port)
|
46
|
+
end
|
51
47
|
|
52
|
-
def
|
53
|
-
|
48
|
+
def hashed_key
|
49
|
+
Kyle.hash_twice(key)
|
54
50
|
end
|
55
51
|
|
52
|
+
def encrypted_inputs
|
53
|
+
Kyle.iterative_encrypt(
|
54
|
+
Kyle.iterative_encrypt(hashed_hostname, hashed_account),
|
55
|
+
Kyle.iterative_encrypt(hashed_port, hashed_key))
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
58
|
+
#################
|
59
|
+
# Class methods #
|
60
|
+
#################
|
61
|
+
def self.encrypt(alg, val, key)
|
62
|
+
cipher = OpenSSL::Cipher::Cipher.new(alg)
|
63
|
+
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(key, 'kyle', 10_000, 32)
|
64
|
+
cipher.iv = sha512ize(key)
|
65
|
+
cipher.encrypt
|
66
|
+
return cipher.update(val) + cipher.final
|
67
|
+
rescue OpenSSL::Cipher::CipherError => e
|
68
|
+
puts "Error: #{e.message}"
|
68
69
|
end
|
69
70
|
|
70
|
-
def self.
|
71
|
+
def self.iterative_encrypt(val, key)
|
71
72
|
ret = val
|
73
|
+
|
72
74
|
val.each_byte do |c|
|
73
|
-
|
75
|
+
alg = Constants::ENC_ALGS[c % Constants::ENC_ALGS.length]
|
76
|
+
ret = encrypt(alg, ret, key)
|
74
77
|
end
|
75
|
-
|
76
|
-
|
78
|
+
|
79
|
+
ret
|
77
80
|
end
|
78
81
|
|
79
|
-
def self.
|
80
|
-
|
81
|
-
OpenSSL::Digest.digest(alg,val)
|
82
|
+
def self.hash(alg, val)
|
83
|
+
OpenSSL::Digest.digest(alg, val)
|
82
84
|
end
|
83
85
|
|
84
86
|
def self.iterative_hash(val)
|
85
87
|
ret = val
|
88
|
+
|
86
89
|
val.each_byte do |c|
|
87
|
-
|
90
|
+
alg = Constants::HASH_ALGS[c % Constants::HASH_ALGS.length]
|
91
|
+
ret = hash(alg, ret)
|
88
92
|
end
|
89
|
-
|
93
|
+
|
94
|
+
ret
|
90
95
|
end
|
91
96
|
|
92
97
|
def self.sha512ize(val)
|
93
|
-
|
98
|
+
hash('sha512', val)
|
94
99
|
end
|
95
100
|
|
96
|
-
def self.
|
101
|
+
def self.hash_twice(val)
|
97
102
|
iterative_hash(iterative_hash(val))
|
98
103
|
end
|
99
104
|
|
100
|
-
def self.
|
101
|
-
ret =
|
102
|
-
|
103
|
-
# to be sure it is long enough
|
104
|
-
h = sha512ize(val)
|
105
|
-
|
106
|
-
h.each_byte do |c|
|
107
|
-
ret += $passctable[c % ($passctable.size)]
|
108
|
-
end
|
109
|
-
|
110
|
-
return ret[0..15]
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def self.generate(hostname,account,port,key)
|
116
|
-
|
117
|
-
ret = Array.new
|
118
|
-
|
119
|
-
harr = [ di_hash(hostname), di_hash(account), di_hash(port), di_hash(key) ]
|
120
|
-
|
121
|
-
v1 = iterative_enc(harr[0],harr[1])
|
122
|
-
v2 = iterative_enc(harr[2],harr[3])
|
105
|
+
def self.hash_to_password(val)
|
106
|
+
ret = ''
|
123
107
|
|
124
|
-
|
125
|
-
|
126
|
-
c = v
|
127
|
-
$animalnames.each do |animal|
|
128
|
-
c = OpenSSL::PKCS5.pbkdf2_hmac_sha1(c, v, 10000, 32)
|
129
|
-
|
130
|
-
ret << hash2pass(c)
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
return ret
|
135
|
-
end
|
136
|
-
|
137
|
-
$testarr = [
|
138
|
-
[
|
139
|
-
"microsoft.com","bill.gates","666","iKnowWhatYouDidToIBMLastSummer",
|
140
|
-
"WW0nQIY.0Rn6D8d2", "nXzU19faM7*gv7,I", "cgu0q7*DuT65r3rY", "ILZ,5vZLl/wRxf9y",
|
141
|
-
"Od..eF2k6_l3XHxe", "sedWPnJsSb4DEJw-", "JEr_SzZBoofgI7Tb", "xbbg@ebdz3FA.n6S",
|
142
|
-
"5EMS!XZP7WfPLKpO", "LcwDHUu0/ynzdSWE", "5+fDOoY.yrE+ESbj", "VWKqetfVKZtT,FI9",
|
143
|
-
"EDe7XvMZ%8tt2vsT", "vks.HPeDVkklS_qb", "hcVznOxvT5YvxIlU", "iBTr-I42uz7h7XnA",
|
144
|
-
"fLV,g6%@1G7xpQil", "toMFvN@Zd,b*KBC%", "FYbi/6Udx_4mO3D0"
|
145
|
-
],
|
146
|
-
[
|
147
|
-
$passctable.join,$passctable.join,$passctable.join,$passctable.join,
|
148
|
-
"cuoTJm!UuIYCcQxs", "3HrQ3s/j53A+Rssm", "KK+IV7QE9Imd65hi", "M@FH%uduAIvn/0Fg",
|
149
|
-
"C@ffelLPbsh!ps68", "mF%j06cgTaD63v5C", "5pRiaZDk%SY6quet", "d56XIGF8PhHu!TfI",
|
150
|
-
"71UES8Six9G48hsc", "kmtu%gr1.k%T!tPy", "zE8*qE+uU.PsOkYY", "77Rbxcaiwp4Fwm.M",
|
151
|
-
"qWT0K%tFP8w7_H0S", "2uGyZI.SzAOujmkw", ".QhaGPzV_jnnRj@F", "4FsOh0GfaM4MVUUH",
|
152
|
-
"_3r0DzAdYvEp@dlA", "qcWgE@nt9SgUEsjP", "8F1BJ.OlV5Kj!wmM"
|
153
|
-
]
|
154
|
-
]
|
155
|
-
|
156
|
-
def self.test_it()
|
157
|
-
puts "Testing..."
|
158
|
-
|
159
|
-
failed = false
|
160
|
-
(0..1).each do |idx|
|
161
|
-
vals = generate($testarr[idx][0],$testarr[idx][1],$testarr[idx][2],$testarr[idx][3])
|
162
|
-
vals.each.with_index(0) do |v,i|
|
163
|
-
if ($testarr[idx][i+4] != v)
|
164
|
-
puts "Test failed: #{$testarr[idx][i+4]} <> #{v}"
|
165
|
-
failed = true
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
puts "Finished #{failed ? "and Failed" : "successfully"}"
|
170
|
-
end
|
171
|
-
|
172
|
-
def self.getkey()
|
173
|
-
|
174
|
-
key = "a"
|
175
|
-
key2 = "b"
|
108
|
+
# to be sure it is long enough
|
109
|
+
hashed = sha512ize(val)
|
176
110
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
if (key != key2)
|
182
|
-
puts "Passes do not match!!"
|
183
|
-
end
|
111
|
+
hashed.each_byte do |c|
|
112
|
+
ret += Constants::PASSWORD_CHARS[c % Constants::PASSWORD_CHARS.length]
|
113
|
+
break if ret.length >= 16
|
184
114
|
end
|
185
115
|
|
186
|
-
|
187
|
-
|
188
|
-
end
|
189
|
-
|
190
|
-
def self.run(args)
|
191
|
-
|
192
|
-
puts "Kyle - A password manager for paranoids. ( 0.0.4 )"
|
193
|
-
puts ""
|
194
|
-
|
195
|
-
if (args.size > 0 && args[0] == "test")
|
196
|
-
test_it()
|
197
|
-
elsif (args.size == 3 && args[0].to_s.downcase == "-b" && args[1] != nil && args[2] != nil)
|
198
|
-
|
199
|
-
# Batch MODE
|
200
|
-
# -b record_file favourite_animal_name
|
201
|
-
|
202
|
-
key = getkey()
|
203
|
-
|
204
|
-
text=File.open(args[1]).read
|
205
|
-
text.gsub!(/\r\n?/, "\n")
|
206
|
-
text.each_line do |line|
|
207
|
-
|
208
|
-
hostname, account, port = line.split(';')
|
209
|
-
|
210
|
-
port = port.gsub(/\n/,"")
|
211
|
-
|
212
|
-
vals = generate(hostname,account,port,key)
|
213
|
-
|
214
|
-
$animalnames.each.with_index(0) do |animal,i|
|
215
|
-
|
216
|
-
puts "#{hostname}:#{account}:#{port} = #{vals[i]}" if (animal.downcase == args[2].downcase)
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
|
221
|
-
end
|
222
|
-
|
223
|
-
else
|
224
|
-
|
225
|
-
hostname = ""
|
226
|
-
account = ""
|
227
|
-
port = ""
|
228
|
-
|
229
|
-
# Record given parameters to .kyle file at home
|
230
|
-
kyle_r_path = File.join(Dir.home,".kyle")
|
231
|
-
|
232
|
-
if (args.size > 0 && args[0].to_s.downcase == "-a")
|
233
|
-
recs = []
|
234
|
-
iA = 0
|
235
|
-
File.open(kyle_r_path).each do |line|
|
236
|
-
recs << line.rstrip!
|
237
|
-
puts "#{iA} - #{line}"
|
238
|
-
iA+=1
|
239
|
-
end
|
240
|
-
|
241
|
-
puts("")
|
242
|
-
idx = ask("Selection:")
|
243
|
-
|
244
|
-
r = recs[idx.to_i].split(";")
|
245
|
-
|
246
|
-
hostname = r[0]
|
247
|
-
account = r[1]
|
248
|
-
port = r[2]
|
249
|
-
|
250
|
-
else
|
251
|
-
hostname = ask("Hostname:")
|
252
|
-
account = ask("Account:")
|
253
|
-
port = ask("Port:")
|
254
|
-
end
|
255
|
-
key = getkey()
|
256
|
-
|
257
|
-
if (args.size > 0 && args[0].to_s.downcase == "-r")
|
258
|
-
|
259
|
-
|
260
|
-
line_to_add = "#{hostname};#{account};#{port}"
|
261
|
-
|
262
|
-
File.open(kyle_r_path, 'a') do |file|
|
263
|
-
file.puts line_to_add
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
puts "Calculating..."
|
268
|
-
vals = generate(hostname,account,port,key)
|
269
|
-
|
270
|
-
$animalnames.each.with_index(0) do |animal,i|
|
271
|
-
|
272
|
-
puts "#{animal}\t#{vals[i]}"
|
273
|
-
|
274
|
-
end
|
275
|
-
end
|
116
|
+
ret[0..15]
|
276
117
|
end
|
277
118
|
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'kyle'
|
2
|
+
|
3
|
+
# Command line access to Kyle
|
4
|
+
class KyleCommands
|
5
|
+
attr_accessor :args
|
6
|
+
|
7
|
+
def initialize(args = [])
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
puts 'Kyle - A password manager for paranoids. ( 0.0.5 )'
|
13
|
+
puts ''
|
14
|
+
|
15
|
+
batch_generate if batch?
|
16
|
+
single_generate_choose if single_by_choose?
|
17
|
+
single_generate if !batch? && !single_by_choose?
|
18
|
+
end
|
19
|
+
|
20
|
+
def ask_for_key
|
21
|
+
return ask('Key:') { |q| q.echo = false } unless check?
|
22
|
+
|
23
|
+
key = 'a'
|
24
|
+
key_check = 'b'
|
25
|
+
|
26
|
+
while key != key_check
|
27
|
+
key = ask('Key:') { |q| q.echo = false }
|
28
|
+
key_check = ask('Key (again):') { |q| q.echo = false }
|
29
|
+
|
30
|
+
puts 'Keys do not match!' unless key == key_check
|
31
|
+
end
|
32
|
+
|
33
|
+
key
|
34
|
+
end
|
35
|
+
|
36
|
+
def batch_generate
|
37
|
+
text = File.open(file).read
|
38
|
+
|
39
|
+
text.gsub!(/\r\n?/, "\n")
|
40
|
+
|
41
|
+
text.each_line do |line|
|
42
|
+
hostname, account, port = line.split(';')
|
43
|
+
|
44
|
+
port.gsub!(/\n/, '')
|
45
|
+
|
46
|
+
passwords = make_passwords(hostname, account, port, key)
|
47
|
+
|
48
|
+
passwords.select { |a, _| animals.include? a }.each do |a, p|
|
49
|
+
puts "#{hostname}:#{account}:#{port} (#{a}) = #{p}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def kyle_r_path
|
55
|
+
File.join(Dir.home, '.kyle')
|
56
|
+
end
|
57
|
+
|
58
|
+
def record
|
59
|
+
line_to_add = "#{hostname};#{account};#{port}"
|
60
|
+
|
61
|
+
File.open(kyle_r_path, 'a') do |file|
|
62
|
+
file.puts line_to_add
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def saved_records
|
67
|
+
recs = []
|
68
|
+
i_a = 0
|
69
|
+
File.open(kyle_r_path).each do |line|
|
70
|
+
recs << line.rstrip!
|
71
|
+
puts "#{i_a} - #{line}"
|
72
|
+
i_a += 1
|
73
|
+
end
|
74
|
+
|
75
|
+
recs
|
76
|
+
end
|
77
|
+
|
78
|
+
def single_generate_choose
|
79
|
+
recs = saved_records
|
80
|
+
|
81
|
+
puts('')
|
82
|
+
idx = ask('Selection:')
|
83
|
+
|
84
|
+
r = recs[idx.to_i].split(';')
|
85
|
+
|
86
|
+
@hostname = r[0]
|
87
|
+
@account = r[1]
|
88
|
+
@port = r[2]
|
89
|
+
|
90
|
+
single_generate
|
91
|
+
end
|
92
|
+
|
93
|
+
def single_generate
|
94
|
+
record if record?
|
95
|
+
|
96
|
+
passwords = make_passwords(hostname, account, port, key)
|
97
|
+
|
98
|
+
if animals.length > 1
|
99
|
+
Constants::ANIMALS.each { |a| puts "#{a}\t#{passwords[a]}" }
|
100
|
+
else
|
101
|
+
puts passwords[animals[0]]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def record?
|
106
|
+
args.include? '-r'
|
107
|
+
end
|
108
|
+
|
109
|
+
def check?
|
110
|
+
(args.include? '-c') || (record?)
|
111
|
+
end
|
112
|
+
|
113
|
+
def batch?
|
114
|
+
args.include? '-b'
|
115
|
+
end
|
116
|
+
|
117
|
+
def single_by_choose?
|
118
|
+
args.include? '-a'
|
119
|
+
end
|
120
|
+
|
121
|
+
def main_args
|
122
|
+
args.reject { |arg| arg[0] == '-' }
|
123
|
+
end
|
124
|
+
|
125
|
+
def hostname
|
126
|
+
@hostname ||= main_args[0] || ask('Hostname:')
|
127
|
+
end
|
128
|
+
|
129
|
+
def account
|
130
|
+
@account ||= main_args[1] || ask('Account:')
|
131
|
+
end
|
132
|
+
|
133
|
+
def port
|
134
|
+
@port ||= main_args[2] || ask('Port:')
|
135
|
+
end
|
136
|
+
|
137
|
+
def animals
|
138
|
+
animal = batch? ? main_args[1] : main_args[3]
|
139
|
+
|
140
|
+
return Constants::ANIMALS if animal.nil?
|
141
|
+
Constants::ANIMALS.select { |a| a.downcase == animal.downcase }
|
142
|
+
end
|
143
|
+
|
144
|
+
def make_passwords(hostname, account, port, key)
|
145
|
+
puts 'Generating...'
|
146
|
+
puts ''
|
147
|
+
|
148
|
+
Kyle.new(hostname, account, port, key).passwords
|
149
|
+
end
|
150
|
+
|
151
|
+
def file
|
152
|
+
fail 'File only for batch operations' unless batch?
|
153
|
+
main_args[0]
|
154
|
+
end
|
155
|
+
|
156
|
+
def key
|
157
|
+
@key ||= ask_for_key
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe KyleCommands do
|
4
|
+
subject(:cmds) { described_class.new(args) }
|
5
|
+
|
6
|
+
context 'in batch mode' do
|
7
|
+
let(:args) { %w(-b filename.txt whale) }
|
8
|
+
|
9
|
+
it 'is in batch mode' do
|
10
|
+
expect(cmds.batch?).to eq(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'runs #batch_generate' do
|
14
|
+
expect(cmds).to receive(:batch_generate)
|
15
|
+
cmds.run
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'in single mode' do
|
20
|
+
let(:args) { %w(microsoft.com evilcommander 8080 panda) }
|
21
|
+
|
22
|
+
it 'is in single mode' do
|
23
|
+
expect(cmds.batch?).to eq(false)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has details set' do
|
27
|
+
expect(cmds.hostname).to eq('microsoft.com')
|
28
|
+
expect(cmds.account).to eq('evilcommander')
|
29
|
+
expect(cmds.port).to eq('8080')
|
30
|
+
expect(cmds.animals).to eq(['Panda'])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/kyle_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Kyle do
|
4
|
+
let!(:passwords) { Kyle.new(hostname, account, port, key).passwords }
|
5
|
+
|
6
|
+
context "Bill Gates' passwords" do
|
7
|
+
let(:hostname) { 'microsoft.com' }
|
8
|
+
let(:account) { 'bill.gates' }
|
9
|
+
let(:port) { '666' }
|
10
|
+
let(:key) { 'iKnowWhatYouDidToIBMLastSummer' }
|
11
|
+
|
12
|
+
it 'has the correct passwords' do
|
13
|
+
expect(passwords).to eq(
|
14
|
+
'Ape' => 'WW0nQIY.0Rn6D8d2',
|
15
|
+
'Bat' => 'nXzU19faM7*gv7,I',
|
16
|
+
'Bear' => 'cgu0q7*DuT65r3rY',
|
17
|
+
'Whale' => 'ILZ,5vZLl/wRxf9y',
|
18
|
+
'Crow' => 'Od..eF2k6_l3XHxe',
|
19
|
+
'Dog' => 'sedWPnJsSb4DEJw-',
|
20
|
+
'Cat' => 'JEr_SzZBoofgI7Tb',
|
21
|
+
'Wasp' => 'xbbg@ebdz3FA.n6S',
|
22
|
+
'Fox' => '5EMS!XZP7WfPLKpO',
|
23
|
+
'Gull' => 'LcwDHUu0/ynzdSWE',
|
24
|
+
'Jackal' => '5+fDOoY.yrE+ESbj',
|
25
|
+
'Lion' => 'VWKqetfVKZtT,FI9',
|
26
|
+
'Panda' => 'EDe7XvMZ%8tt2vsT',
|
27
|
+
'Rat' => 'vks.HPeDVkklS_qb',
|
28
|
+
'Shark' => 'hcVznOxvT5YvxIlU',
|
29
|
+
'Spider' => 'iBTr-I42uz7h7XnA',
|
30
|
+
'Turtle' => 'fLV,g6%@1G7xpQil',
|
31
|
+
'Wolf' => 'toMFvN@Zd,b*KBC%',
|
32
|
+
'Zebra' => 'FYbi/6Udx_4mO3D0'
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with crazy inputs' do
|
38
|
+
let(:hostname) { Constants::PASSWORD_CHARS.join }
|
39
|
+
let(:account) { Constants::PASSWORD_CHARS.join }
|
40
|
+
let(:port) { Constants::PASSWORD_CHARS.join }
|
41
|
+
let(:key) { Constants::PASSWORD_CHARS.join }
|
42
|
+
|
43
|
+
it 'has correct passwords' do
|
44
|
+
expect(passwords).to eq(
|
45
|
+
'Ape' => 'cuoTJm!UuIYCcQxs',
|
46
|
+
'Bat' => '3HrQ3s/j53A+Rssm',
|
47
|
+
'Bear' => 'KK+IV7QE9Imd65hi',
|
48
|
+
'Whale' => 'M@FH%uduAIvn/0Fg',
|
49
|
+
'Crow' => 'C@ffelLPbsh!ps68',
|
50
|
+
'Dog' => 'mF%j06cgTaD63v5C',
|
51
|
+
'Cat' => '5pRiaZDk%SY6quet',
|
52
|
+
'Wasp' => 'd56XIGF8PhHu!TfI',
|
53
|
+
'Fox' => '71UES8Six9G48hsc',
|
54
|
+
'Gull' => 'kmtu%gr1.k%T!tPy',
|
55
|
+
'Jackal' => 'zE8*qE+uU.PsOkYY',
|
56
|
+
'Lion' => '77Rbxcaiwp4Fwm.M',
|
57
|
+
'Panda' => 'qWT0K%tFP8w7_H0S',
|
58
|
+
'Rat' => '2uGyZI.SzAOujmkw',
|
59
|
+
'Shark' => '.QhaGPzV_jnnRj@F',
|
60
|
+
'Spider' => '4FsOh0GfaM4MVUUH',
|
61
|
+
'Turtle' => '_3r0DzAdYvEp@dlA',
|
62
|
+
'Wolf' => 'qcWgE@nt9SgUEsjP',
|
63
|
+
'Zebra' => '8F1BJ.OlV5Kj!wmM'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kyle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harun Esur
|
8
|
+
- Isaac Seymour
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2014-
|
12
|
+
date: 2014-09-17 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: highline
|
@@ -30,6 +31,34 @@ dependencies:
|
|
30
31
|
- - ">="
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: 1.6.20
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rubocop
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.26'
|
41
|
+
type: :development
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.26'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.0.0
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.0.0
|
33
62
|
description: A password manager for paranoids.
|
34
63
|
email: harun.esur@sceptive.com
|
35
64
|
executables:
|
@@ -37,8 +66,19 @@ executables:
|
|
37
66
|
extensions: []
|
38
67
|
extra_rdoc_files: []
|
39
68
|
files:
|
40
|
-
-
|
69
|
+
- ".gitignore"
|
70
|
+
- Gemfile
|
71
|
+
- Gemfile.lock
|
72
|
+
- LICENSE
|
73
|
+
- README.md
|
41
74
|
- bin/kyle
|
75
|
+
- kyle.gemspec
|
76
|
+
- lib/constants.rb
|
77
|
+
- lib/kyle.rb
|
78
|
+
- lib/kyle_commands.rb
|
79
|
+
- spec/kyle_commands_spec.rb
|
80
|
+
- spec/kyle_spec.rb
|
81
|
+
- spec/spec_helper.rb
|
42
82
|
homepage: http://sceptive.com
|
43
83
|
licenses:
|
44
84
|
- MIT
|