krypter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7e0dfa5c8ff00e53057804420023fe37efb06f4b
4
+ data.tar.gz: b7daec95060bf484e2203fce88174f383375a0b6
5
+ SHA512:
6
+ metadata.gz: 8260c08cb857ee43e15a2652bfb3a17141cae99c6e9345a9772eb7b5e2dcf2d7f0e5fbf339a97674a901e7c9b9eb9a1a9a672bf9197c8556922d5128f9066d8a
7
+ data.tar.gz: f0a2c5226ea6b3add326e8760398d80f07c8a2073208aa4f60ff0d90acb91148cfecbd6c1c3feae3526f3232ca4754ef050ebb226a4190a67f9cb5ebad34f9d3
data/.gems ADDED
@@ -0,0 +1 @@
1
+ cutest -v 1.2.1
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Francesco Rodríguez
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.
@@ -0,0 +1,54 @@
1
+ krypter
2
+ =======
3
+
4
+ Encrypts messages with authentication.
5
+
6
+ Usage
7
+ -----
8
+
9
+ Pass a secret token. This must be at least 32 bytes
10
+ long and should be really random. You can generate
11
+ a random secret with `SecureRandom.hex(32)`.
12
+
13
+ ```ruby
14
+ require "securerandom"
15
+ require "krypter"
16
+
17
+ secret = SecureRandom.hex(32)
18
+ encryptor = Krypter.new(secret)
19
+ encrypted = encryptor.encrypt("message")
20
+ encryptor.decrypt(encrypted) == "message"
21
+ # => true
22
+
23
+ # If the signature is invalid, it raises a `InvalidSignature` error.
24
+ encryptor.decrypt("")
25
+ # => Krypter::InvalidSignature
26
+
27
+ # If the message is changed, it raises a `InvalidMessage` error.
28
+ ciphertext, signature = encrypted.split("--")
29
+ ciphertext.reverse!
30
+
31
+ encryptor.decrypt([ciphertext, signature].join("--"))
32
+ # => Krypter::InvalidMessage
33
+ ```
34
+
35
+ By default, the messages are encrypted with 256-bit AES in CBC mode
36
+ (with random IV). The encrypted message is then signed with HMAC-SHA256,
37
+ to prevent tampering and chosen ciphertext attacks.
38
+
39
+ The defaults can be changed when instantiating the encryptor object.
40
+
41
+ ```ruby
42
+ encryptor = Encryptor.new(secret,
43
+ cipher: "aes-256-cbc",
44
+ digest: "SHA256",
45
+ separator: "--"
46
+ )
47
+ ```
48
+
49
+ Installation
50
+ ------------
51
+
52
+ ```
53
+ $ gem install krypter
54
+ ```
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "krypter"
3
+ s.version = "1.0.0"
4
+ s.summary = "Encrypts and signs messages."
5
+ s.description = s.summary
6
+ s.authors = ["Francesco Rodríguez", "Mayn Kjær"]
7
+ s.email = ["frodsan@me.com", "mayn.kjaer@gmail.com"]
8
+ s.homepage = "https://github.com/harmoni-io/krypter"
9
+ s.license = "MIT"
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+
13
+ s.add_development_dependency "cutest"
14
+ end
@@ -0,0 +1,96 @@
1
+ require "base64"
2
+ require "openssl"
3
+
4
+ class Krypter
5
+ InvalidSignature = Class.new(StandardError)
6
+ InvalidMessage = Class.new(StandardError)
7
+
8
+ def initialize(secret, cipher: "aes-256-cbc", digest: "SHA256", separator: "--")
9
+ @cipher = cipher
10
+ @digest = digest
11
+ @separator = separator
12
+ @encrypt_secret = hmac(secret, "encryption key")
13
+ @sign_secret = hmac(secret, "signin key")
14
+ end
15
+
16
+ def encrypt(message)
17
+ return sign(_encrypt(message))
18
+ end
19
+
20
+ def decrypt(message)
21
+ ciphertext = verify(message)
22
+
23
+ if ciphertext
24
+ return _decrypt(ciphertext)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def hmac(secret, message)
31
+ return OpenSSL::HMAC.hexdigest(@digest, secret, message)
32
+ end
33
+
34
+ def _encrypt(message)
35
+ cipher = OpenSSL::Cipher.new(@cipher)
36
+ cipher.encrypt
37
+ cipher.key = @encrypt_secret
38
+
39
+ iv = cipher.random_iv
40
+ ciphertext = cipher.update(message)
41
+ ciphertext << cipher.final
42
+
43
+ return [ciphertext, iv].join(@separator)
44
+ end
45
+
46
+ def _decrypt(encrypted)
47
+ ciphertext, iv = encrypted.split(@separator)
48
+
49
+ decipher = OpenSSL::Cipher.new(@cipher)
50
+ decipher.decrypt
51
+ decipher.key = @encrypt_secret
52
+ decipher.iv = iv
53
+
54
+ decrypted = decipher.update(ciphertext)
55
+ decrypted << decipher.final
56
+
57
+ return decrypted
58
+ rescue OpenSSL::Cipher::CipherError
59
+ raise InvalidMessage
60
+ end
61
+
62
+ def sign(value)
63
+ encoded = Base64.strict_encode64(value)
64
+ signature = authenticate(encoded)
65
+
66
+ return [encoded, signature].join(@separator)
67
+ end
68
+
69
+ def authenticate(message)
70
+ return hmac(@sign_secret, message)
71
+ end
72
+
73
+ def verify(message)
74
+ value, signature = message.split(@separator)
75
+
76
+ if value && signature && secure_compare(signature, authenticate(value))
77
+ return Base64.strict_decode64(value)
78
+ else
79
+ raise InvalidSignature
80
+ end
81
+ end
82
+
83
+ # Prevents timing attacks: http://codahale.com/a-lesson-in-timing-attacks/.
84
+ def secure_compare(a, b)
85
+ return false unless a.bytesize == b.bytesize
86
+
87
+ cmp = b.bytes
88
+ result = 0
89
+
90
+ a.bytes.each_with_index do |char, index|
91
+ result |= char ^ cmp[index]
92
+ end
93
+
94
+ return result == 0
95
+ end
96
+ end
@@ -0,0 +1,4 @@
1
+ .PHONY: test
2
+
3
+ test:
4
+ cutest test/*.rb
@@ -0,0 +1,73 @@
1
+ require "cutest"
2
+ require "securerandom"
3
+ require_relative "../lib/krypter"
4
+
5
+ setup do
6
+ Krypter.new(SecureRandom.hex(32))
7
+ end
8
+
9
+ test "encrypts and decrypts" do |encryptor|
10
+ encrypted = encryptor.encrypt("message")
11
+ decrypted = encryptor.decrypt(encrypted)
12
+
13
+ assert_equal("message", decrypted)
14
+ end
15
+
16
+ test "encrypt returns different ciphertexts" do |encryptor|
17
+ encrypted1 = encryptor.encrypt("message")
18
+ encrypted2 = encryptor.encrypt("message")
19
+
20
+ assert encrypted1 != encrypted2
21
+ end
22
+
23
+ test "wrong signature" do |encryptor|
24
+ encrypted = encryptor.encrypt("message")
25
+ separator = encryptor.instance_variable_get(:@separator)
26
+ ciphertext, signature = encrypted.split(separator)
27
+
28
+ message = [signature, ciphertext] * separator
29
+ assert_raise(Krypter::InvalidSignature) do
30
+ encryptor.decrypt(message)
31
+ end
32
+
33
+ message = [ciphertext, signature.reverse] * separator
34
+ assert_raise(Krypter::InvalidSignature) do
35
+ encryptor.decrypt(message)
36
+ end
37
+
38
+ message = [ciphertext.reverse, signature] * separator
39
+ assert_raise(Krypter::InvalidSignature) do
40
+ encryptor.decrypt(message)
41
+ end
42
+
43
+ message = [ciphertext.reverse, signature.reverse] * separator
44
+ assert_raise(Krypter::InvalidSignature) do
45
+ encryptor.decrypt(message)
46
+ end
47
+ end
48
+
49
+ test "tampered data" do |encryptor|
50
+ encrypted = encryptor.encrypt("message")
51
+ separator = encryptor.instance_variable_get(:@separator)
52
+ ciphertext, iv = encryptor.send(:verify, encrypted).split(separator)
53
+
54
+ message = encryptor.send(:sign, [iv, ciphertext] * separator)
55
+ assert_raise(Krypter::InvalidMessage) do
56
+ encryptor.decrypt(message)
57
+ end
58
+
59
+ message = encryptor.send(:sign, [ciphertext, iv.reverse] * separator)
60
+ assert_raise(Krypter::InvalidMessage) do
61
+ encryptor.decrypt(message)
62
+ end
63
+
64
+ message = encryptor.send(:sign, [ciphertext.reverse, iv] * separator)
65
+ assert_raise(Krypter::InvalidMessage) do
66
+ encryptor.decrypt(message)
67
+ end
68
+
69
+ message = encryptor.send(:sign, [ciphertext.reverse, iv.reverse] * separator)
70
+ assert_raise(Krypter::InvalidMessage) do
71
+ encryptor.decrypt(message)
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: krypter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Francesco Rodríguez
8
+ - Mayn Kjær
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cutest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ description: Encrypts and signs messages.
29
+ email:
30
+ - frodsan@me.com
31
+ - mayn.kjaer@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gems"
37
+ - LICENSE
38
+ - README.md
39
+ - krypter.gemspec
40
+ - lib/krypter.rb
41
+ - makefile
42
+ - test/krypter.rb
43
+ homepage: https://github.com/harmoni-io/krypter
44
+ licenses:
45
+ - MIT
46
+ metadata: {}
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project:
63
+ rubygems_version: 2.2.2
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Encrypts and signs messages.
67
+ test_files: []