armor 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README +17 -0
- data/bin/armor +16 -0
- data/lib/armor.rb +107 -0
- data/makefile +2 -0
- data/tests/armor_test.rb +33 -0
- data/tests/rfc3962_test.rb +75 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d38e8f64ee2202c50a7ba0112760c891afab81df
|
4
|
+
data.tar.gz: 6218494c71b80ec48173c37e7cd33ae5b2f0bcba
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bd75de939639c9bb1d5c946f8213e2f85fa8a327f0e1e6c38b31a8aa93cb47a18d605bec91fa4b5dc72ff61bc97d4c142e208b28f4700e6707c7c674c7e30316
|
7
|
+
data.tar.gz: 724e0ae4b2b91aec61d6405d57054f777d4da2217dd68b4db22e8d26e2a7216018a94602d934507fca65c2281c5603ae88067c1787320b77406956c3410e49b1
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Cyril David, Michel Martens
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
data/bin/armor
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path("../lib/armor", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
if ARGV.empty?
|
6
|
+
abort("Usage: armor [N] # Where N = number of iterations")
|
7
|
+
end
|
8
|
+
|
9
|
+
require "benchmark"
|
10
|
+
|
11
|
+
t = Benchmark.realtime do
|
12
|
+
ENV["ARMOR_ITER"] = ARGV[0]
|
13
|
+
Armor.digest("password", "EXAMPLE.COMpianist")
|
14
|
+
end
|
15
|
+
|
16
|
+
puts "Iterations: %d, Time: %.2f" % [ARGV[0], t]
|
data/lib/armor.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require "openssl"
|
2
|
+
|
3
|
+
module Armor
|
4
|
+
# Syntactic sugar for the underlying mathematical definition of PBKDF2.
|
5
|
+
#
|
6
|
+
# This function does not allow passing in of a dkLen (in other words
|
7
|
+
# does not allow truncation of the final derived key).
|
8
|
+
#
|
9
|
+
# Returns: Binary representation of the derived key (DK).
|
10
|
+
#
|
11
|
+
# The default value for iter is set to 5000, and it can be
|
12
|
+
# configured via `ENV['ARMOR_ITER']`.
|
13
|
+
#
|
14
|
+
# The default value for hash is "sha512", and it can also be
|
15
|
+
# configured via `ENV['ARMOR_HASH']`.
|
16
|
+
def self.digest(password, salt)
|
17
|
+
iter = ENV["ARMOR_ITER"] || 5000
|
18
|
+
hash = ENV["ARMOR_HASH"] || "sha512"
|
19
|
+
|
20
|
+
digest = OpenSSL::Digest::Digest.new(hash)
|
21
|
+
length = digest.digest_length
|
22
|
+
|
23
|
+
hex(pbkdf2(digest, password, salt, Integer(iter), length))
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.randomize(digest, password, seed)
|
27
|
+
OpenSSL::HMAC.digest(digest, password, seed)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Binary to hex convenience method.
|
31
|
+
def self.hex(str)
|
32
|
+
str.unpack("H*").first
|
33
|
+
end
|
34
|
+
|
35
|
+
# The PBKDF2 key derivation function has five input parameters:
|
36
|
+
#
|
37
|
+
# DK = PBKDF2(PRF, Password, Salt, c, dkLen)
|
38
|
+
#
|
39
|
+
# where:
|
40
|
+
#
|
41
|
+
# - PRF is a pseudorandom function of two parameters
|
42
|
+
# - Password is the master password from which a derived key is generated
|
43
|
+
# - Salt is a cryptographic salt
|
44
|
+
# - c is the number of iterations desired
|
45
|
+
# - dkLen is the desired length of the derived key
|
46
|
+
#
|
47
|
+
# DK is the generated derived key.
|
48
|
+
#
|
49
|
+
def self.pbkdf2(digest, password, salt, c, dk_len)
|
50
|
+
blocks_needed = (dk_len.to_f / digest.size).ceil
|
51
|
+
|
52
|
+
result = ""
|
53
|
+
|
54
|
+
# main block-calculating loop:
|
55
|
+
1.upto(blocks_needed) do |n|
|
56
|
+
result << concatenate(digest, password, salt, c, n)
|
57
|
+
end
|
58
|
+
|
59
|
+
# truncate to desired length:
|
60
|
+
result.slice(0, dk_len)
|
61
|
+
end
|
62
|
+
|
63
|
+
# The function F is the xor (^) of c iterations of chained PRFs.
|
64
|
+
# The first iteration of PRF uses Password as the PRF key and Salt
|
65
|
+
# concatenated to i encoded as a big-endian 32-bit integer.
|
66
|
+
#
|
67
|
+
# Note that i is a 1-based index. Subsequent iterations of PRF use
|
68
|
+
# Password as the PRF key and the output of the previous PRF
|
69
|
+
# computation as the salt:
|
70
|
+
#
|
71
|
+
# Definition:
|
72
|
+
#
|
73
|
+
# F(Password, Salt, Iterations, i) = U1 ^ U2 ^ ... ^ Uc
|
74
|
+
#
|
75
|
+
def self.concatenate(digest, password, salt, iterations, i)
|
76
|
+
|
77
|
+
# U1 -> password, salt and 1 encoded as big-endian 32-bit integer.
|
78
|
+
u = randomize(digest, password, salt + [i].pack("N"))
|
79
|
+
|
80
|
+
ret = u
|
81
|
+
|
82
|
+
# U2 through Uc:
|
83
|
+
2.upto(iterations) do
|
84
|
+
|
85
|
+
# calculate Un
|
86
|
+
u = randomize(digest, password, u)
|
87
|
+
|
88
|
+
# xor it with the previous results
|
89
|
+
ret = xor(ret, u)
|
90
|
+
end
|
91
|
+
|
92
|
+
ret
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def self.xor(a, b)
|
97
|
+
result = "".encode("ASCII-8BIT")
|
98
|
+
|
99
|
+
b_bytes = b.bytes.to_a
|
100
|
+
|
101
|
+
a.bytes.each_with_index do |c, i|
|
102
|
+
result << (c ^ b_bytes[i])
|
103
|
+
end
|
104
|
+
|
105
|
+
result
|
106
|
+
end
|
107
|
+
end
|
data/makefile
ADDED
data/tests/armor_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path("../lib/armor", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "length assertions" do
|
4
|
+
ENV["ARMOR_HASH"] = "sha1"
|
5
|
+
assert_equal 40, Armor.digest("monkey", "salt").length
|
6
|
+
|
7
|
+
ENV["ARMOR_HASH"] = "sha256"
|
8
|
+
assert_equal 64, Armor.digest("monkey", "salt").length
|
9
|
+
|
10
|
+
ENV["ARMOR_HASH"] = "sha512"
|
11
|
+
assert_equal 128, Armor.digest("monkey", "salt").length
|
12
|
+
end
|
13
|
+
|
14
|
+
# The old library PBKDF2 used to have bugs related to iteration count.
|
15
|
+
#
|
16
|
+
# Let's add a regression test to make sure we never get the problem.
|
17
|
+
test "iterations" do
|
18
|
+
ENV["ARMOR_ITER"] = "1"
|
19
|
+
assert_equal 128, Armor.digest("monkey", "salt").length
|
20
|
+
|
21
|
+
ENV["ARMOR_ITER"] = "2"
|
22
|
+
assert_equal 128, Armor.digest("monkey", "salt").length
|
23
|
+
|
24
|
+
ENV["ARMOR_ITER"] = "5000"
|
25
|
+
assert_equal 128, Armor.digest("monkey", "salt").length
|
26
|
+
end
|
27
|
+
|
28
|
+
test "equality of identical keys" do
|
29
|
+
a = Armor.digest("monkey", "salt")
|
30
|
+
b = Armor.digest("monkey", "salt")
|
31
|
+
|
32
|
+
assert_equal a, b
|
33
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path("../lib/armor", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
# see http://www.rfc-archive.org/getrfc.php?rfc=3962
|
4
|
+
# all examples there use HMAC-SHA1
|
5
|
+
|
6
|
+
test "first test case in appendix B of RFC 3962" do
|
7
|
+
# Iteration count = 1
|
8
|
+
#
|
9
|
+
# Pass phrase = "password"
|
10
|
+
#
|
11
|
+
# Salt = "ATHENA.MIT.EDUraeburn"
|
12
|
+
#
|
13
|
+
# 128-bit PBKDF2 output:
|
14
|
+
# cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15
|
15
|
+
#
|
16
|
+
# 256-bit PBKDF2 output:
|
17
|
+
# cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15
|
18
|
+
# 0a d1 f7 a0 4b b9 f3 a3 33 ec c0 e2 e1 f7 08 37
|
19
|
+
|
20
|
+
expected = "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15 0a d1 f7 a0"
|
21
|
+
|
22
|
+
ENV["ARMOR_ITER"] = "1"
|
23
|
+
ENV["ARMOR_HASH"] = "sha1"
|
24
|
+
actual = Armor.digest("password", "ATHENA.MIT.EDUraeburn")
|
25
|
+
|
26
|
+
assert_equal expected.tr(' ', ''), actual
|
27
|
+
end
|
28
|
+
|
29
|
+
test "second test case in appendix B of RFC 3962" do
|
30
|
+
# Iteration count = 2
|
31
|
+
# Pass phrase = "password"
|
32
|
+
# Salt="ATHENA.MIT.EDUraeburn"
|
33
|
+
#
|
34
|
+
# 128-bit PBKDF2 output:
|
35
|
+
# 01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d
|
36
|
+
#
|
37
|
+
# 256-bit PBKDF2 output:
|
38
|
+
# 01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d
|
39
|
+
# a0 53 78 b9 32 44 ec 8f 48 a9 9e 61 ad 79 9d 86
|
40
|
+
|
41
|
+
ENV["ARMOR_ITER"] = "2"
|
42
|
+
ENV["ARMOR_HASH"] = "sha1"
|
43
|
+
actual = Armor.digest("password", "ATHENA.MIT.EDUraeburn")
|
44
|
+
|
45
|
+
expected = "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d a0 53 78 b9"
|
46
|
+
|
47
|
+
assert_equal expected.tr(' ', ''), actual
|
48
|
+
end
|
49
|
+
|
50
|
+
test "should match the third test case in appendix B of RFC 3962" do
|
51
|
+
# Iteration count = 1200
|
52
|
+
# Pass phrase = "password"
|
53
|
+
# Salt = "ATHENA.MIT.EDUraeburn"
|
54
|
+
#
|
55
|
+
# 128-bit PBKDF2 output:
|
56
|
+
# 5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b
|
57
|
+
#
|
58
|
+
# 256-bit PBKDF2 output:
|
59
|
+
# 5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b
|
60
|
+
# a7 e5 2d db c5 e5 14 2f 70 8a 31 e2 e6 2b 1e 13
|
61
|
+
|
62
|
+
digest = OpenSSL::Digest::Digest.new("sha1")
|
63
|
+
|
64
|
+
actual = Armor.pbkdf2(digest, "password", "ATHENA.MIT.EDUraeburn", 1200, 16)
|
65
|
+
|
66
|
+
expected = "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b"
|
67
|
+
assert_equal expected.tr(' ', ''), Armor.hex(actual)
|
68
|
+
|
69
|
+
expected = "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b" +
|
70
|
+
"a7 e5 2d db c5 e5 14 2f 70 8a 31 e2 e6 2b 1e 13"
|
71
|
+
|
72
|
+
actual = Armor.pbkdf2(digest, "password", "ATHENA.MIT.EDUraeburn", 1200, 32)
|
73
|
+
|
74
|
+
assert_equal expected.tr(' ', ''), Armor.hex(actual)
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: armor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cyril David
|
8
|
+
- Michel Martens
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-13 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A PBKDF2 pure ruby implementation.
|
15
|
+
email:
|
16
|
+
- cyx@cyx.is
|
17
|
+
- michel@soveran.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- README
|
23
|
+
- LICENSE
|
24
|
+
- makefile
|
25
|
+
- lib/armor.rb
|
26
|
+
- tests/armor_test.rb
|
27
|
+
- tests/rfc3962_test.rb
|
28
|
+
- bin/armor
|
29
|
+
homepage: http://github.com/cyx/armor
|
30
|
+
licenses:
|
31
|
+
- MIT
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.0.0
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: shield's partner in crime.
|
53
|
+
test_files: []
|