serket 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -0
- data/lib/serket/configuration.rb +13 -1
- data/lib/serket/decrypted_fields.rb +19 -0
- data/lib/serket/encrypted_fields.rb +20 -0
- data/lib/serket/field_decrypter.rb +19 -2
- data/lib/serket/field_encrypter.rb +11 -3
- data/lib/serket/version.rb +1 -1
- data/spec/serket/field_encrypter_spec.rb +20 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9add635d8b06b59c7bc47526c0b22b90b9bedd17
|
4
|
+
data.tar.gz: b357446a204828c351bee22546f4e4f1365d8fe6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c613a58ca1b059019288a0240a44901d97697ab5fa6f76755a6c31816dcffe0b2ebaaf4e3f58c7487e20336ed927229bd77e282e1423cf2be890e24e7db91b1
|
7
|
+
data.tar.gz: d203cd2cc7363a52aee031cf942d96d01e8eb37d66f8080111d881a619217e5e90f0db4d99acf67d228dc7dc5e1e541503c47dfc326eb2447d7fb57599cd3335
|
data/README.md
CHANGED
@@ -76,6 +76,7 @@ There are a few more configuration options.
|
|
76
76
|
| format | :delimited | :delimited, :json |
|
77
77
|
| symmetric_algorithm | AES-256-CBC | Any valid cipher from OpenSSL::Cipher |
|
78
78
|
| delimiter | :: | Anything not base64 |
|
79
|
+
| encoding | utf-8 | Any valid ruby encoding |
|
79
80
|
|
80
81
|
These can all be modified in the configuration block, eg:
|
81
82
|
|
@@ -126,6 +127,15 @@ Serket.configure do |config|
|
|
126
127
|
end
|
127
128
|
```
|
128
129
|
|
130
|
+
You can do a sanity-check with your api endpoint by sending a post request for a model with decrypted_fields:
|
131
|
+
```
|
132
|
+
curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"decrypted_model": {"name": "bm5gkjwdfv6QBUqFPEnOaw==::CtQJyXdbuLeVzmIOIr0h/F9yh7xvz5KWUgxe/u4IkORGOW4KjU4bw+Wzve2vV1nLYUEWJJprr8sb+grm+Ao2sngNejiHzSkJKqZA/Pclw/Ok8KgHgN7olUz4BoCSdivIDRIT9ar06sNBrqOvLd4iGUlpMkpLdSJ69K08ebSvg5tED+PcK/oI6SJoVxRoUMYdYa9AfeIS9Ld5BgvhsaJgCKr089kfH2CzwpzlmRfdxb2qgyDXnk9PG/4WUEjjbamF/R74FNBdWkTLxZeLGdMImh87CQ6AOJ/v8l1JSzpPWwEjtmhTbFEzJPuA01tP5U5D07si0esJnab/B48iACEoLg==::iSmdDgnTzkEUv0yLbtFa8Q=="}}' http://localhost:3000/api/v1/decrypted_models
|
133
|
+
```
|
134
|
+
|
135
|
+
This should automatically decrypt the name for the decrypted_model before saving if you use the provided private key located in spec/resources/test_private_key.pem (and keep the default configs for delimited with ::)
|
136
|
+
|
137
|
+
**Just be sure to use your own keys in production!**
|
138
|
+
|
129
139
|
### Android Java Client
|
130
140
|
|
131
141
|
You can see an example java client for use with Android in EncryptUtil.java
|
data/lib/serket/configuration.rb
CHANGED
@@ -1,11 +1,23 @@
|
|
1
1
|
module Serket
|
2
|
+
# Controls various configuration options such as key locations, desired
|
3
|
+
# algorithms and expected cipher text format.
|
4
|
+
#
|
5
|
+
# * +private_key_path+ - The filepath to a RSA private key
|
6
|
+
# * +public_key_path+ - The filepath to a RSA public key
|
7
|
+
# * +delimiter+ - If the format is set to :delimited, use this delimiter.
|
8
|
+
# Must not be a character in base64.
|
9
|
+
# * +format+ - May be :delimited or :json
|
10
|
+
# * +symmetric_algorithm+ - May be any algorithm that may be used with OpenSSL::Cipher
|
11
|
+
# * +encoding+ - Any valid ruby encoding
|
12
|
+
#
|
2
13
|
class Configuration
|
3
|
-
attr_accessor :private_key_path, :public_key_path, :delimiter, :format, :symmetric_algorithm
|
14
|
+
attr_accessor :private_key_path, :public_key_path, :delimiter, :format, :symmetric_algorithm, :encoding
|
4
15
|
|
5
16
|
def initialize
|
6
17
|
@delimiter = "::"
|
7
18
|
@format = :delimited
|
8
19
|
@symmetric_algorithm = 'AES-256-CBC'
|
20
|
+
@encoding = 'utf-8'
|
9
21
|
end
|
10
22
|
end
|
11
23
|
end
|
@@ -1,4 +1,23 @@
|
|
1
1
|
module Serket
|
2
|
+
# If a class extends DecryptedFields, then the getter method will be overridden
|
3
|
+
# for each attribute that is listed.
|
4
|
+
#
|
5
|
+
# For example:
|
6
|
+
#
|
7
|
+
# class Person
|
8
|
+
# extend Serket::DecryptedFields
|
9
|
+
#
|
10
|
+
# decrypted_fields :name, :email
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# This will create a method name=(value) and email=(value)
|
14
|
+
#
|
15
|
+
# It assumes the encrypted name and email will be assigned to an
|
16
|
+
# instance of person, and will automatically decrypt the encrypted
|
17
|
+
# value using the configured private key.
|
18
|
+
#
|
19
|
+
# This currently relies on the write_attribute, which is available in
|
20
|
+
# ActiveRecord::Base. This currently is only intended for use with rails.
|
2
21
|
module DecryptedFields
|
3
22
|
def decrypted_fields(*fields)
|
4
23
|
fields.each do |field|
|
@@ -1,4 +1,24 @@
|
|
1
1
|
module Serket
|
2
|
+
# If a class extends EncryptedFields, then the getter method will be overridden
|
3
|
+
# for each attribute that is listed.
|
4
|
+
#
|
5
|
+
# For example:
|
6
|
+
#
|
7
|
+
# class Person
|
8
|
+
# extend Serket::EncryptedFields
|
9
|
+
#
|
10
|
+
# encrypted_fields :name, :email
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# This will create a method name=(value) and email=(value)
|
14
|
+
#
|
15
|
+
# When a value is assigned to name or email for a given person instance,
|
16
|
+
# it will automatically generate an encrypted version using the
|
17
|
+
# currently configured public key, and will store the cipher text instead
|
18
|
+
# of the plaintext value that was assigned.
|
19
|
+
#
|
20
|
+
# This currently relies on the write_attribute, which is available in
|
21
|
+
# ActiveRecord::Base. This currently is only intended for use with rails.
|
2
22
|
module EncryptedFields
|
3
23
|
def encrypted_fields(*fields)
|
4
24
|
fields.each do |field|
|
@@ -2,8 +2,10 @@ require 'openssl'
|
|
2
2
|
require 'base64'
|
3
3
|
|
4
4
|
module Serket
|
5
|
+
# Used to decrypt a field given a private key, field delimiter, symmetric
|
6
|
+
# algorithm, encoding, and format (:json or :delimited)
|
5
7
|
class FieldDecrypter
|
6
|
-
attr_accessor :field_delimiter, :private_key_filepath, :symmetric_algorithm
|
8
|
+
attr_accessor :field_delimiter, :private_key_filepath, :symmetric_algorithm, :encoding
|
7
9
|
|
8
10
|
def initialize(options = {})
|
9
11
|
options ||= {}
|
@@ -12,16 +14,23 @@ module Serket
|
|
12
14
|
@field_delimiter = options[:field_delimiter] || Serket.configuration.delimiter
|
13
15
|
@symmetric_algorithm = options[:symmetric_algorithm] || Serket.configuration.symmetric_algorithm
|
14
16
|
@format = options[:format] || Serket.configuration.format
|
17
|
+
@encoding = options[:encoding] || Serket.configuration.encoding
|
15
18
|
end
|
16
19
|
|
20
|
+
# Decrypt the provided cipher text, and return the plaintext
|
21
|
+
# Return nil if whitespace
|
17
22
|
def decrypt(field)
|
18
23
|
return if field !~ /\S/
|
19
24
|
iv, encrypted_aes_key, encrypted_text = parse(field)
|
20
25
|
private_key = OpenSSL::PKey::RSA.new(File.read(private_key_filepath))
|
21
26
|
decrypted_aes_key = private_key.private_decrypt(Base64.decode64(encrypted_aes_key))
|
22
|
-
decrypt_data(iv, decrypted_aes_key, encrypted_text)
|
27
|
+
decrypted_field = decrypt_data(iv, decrypted_aes_key, encrypted_text)
|
28
|
+
decrypted_field.force_encoding(encoding)
|
23
29
|
end
|
24
30
|
|
31
|
+
# What delimiter to use if the format is :delimited.
|
32
|
+
#
|
33
|
+
# Allow anything that is not base64.
|
25
34
|
def field_delimiter=(delimiter)
|
26
35
|
if delimiter =~ /[A-Za-z0-9\/+]/
|
27
36
|
raise "This is not a valid delimiter! Must not be a character in Base64."
|
@@ -39,6 +48,14 @@ module Serket
|
|
39
48
|
aes.update(Base64.decode64(encrypted_text)) + aes.final
|
40
49
|
end
|
41
50
|
|
51
|
+
# Extracts the initialization vector, encrypted key, and
|
52
|
+
# cipher text according to the specified format.
|
53
|
+
#
|
54
|
+
# delimited:
|
55
|
+
# * Expected format: iv::encrypted-key::ciphertext
|
56
|
+
#
|
57
|
+
# json:
|
58
|
+
# * Expected keys: iv, key, message
|
42
59
|
def parse(field)
|
43
60
|
case @format
|
44
61
|
when :delimited
|
@@ -2,26 +2,32 @@ require 'openssl'
|
|
2
2
|
require 'base64'
|
3
3
|
|
4
4
|
module Serket
|
5
|
+
# Used to encrypt a field given a public key, field delimiter, symmetric
|
6
|
+
# algorithm, encoding and format (:json or :delimited)
|
5
7
|
class FieldEncrypter
|
6
|
-
attr_accessor :field_delimiter, :public_key_filepath, :symmetric_algorithm
|
8
|
+
attr_accessor :field_delimiter, :public_key_filepath, :symmetric_algorithm, :encoding
|
7
9
|
|
8
10
|
def initialize(options = {})
|
9
11
|
options ||= {}
|
10
12
|
|
11
|
-
@public_key_filepath
|
13
|
+
@public_key_filepath = Serket.configuration.public_key_path
|
12
14
|
@field_delimiter = options[:field_delimiter] || Serket.configuration.delimiter
|
13
15
|
@symmetric_algorithm = options[:symmetric_algorithm] || Serket.configuration.symmetric_algorithm
|
14
16
|
@format = options[:format] || Serket.configuration.format
|
17
|
+
@encoding = options[:encoding] || Serket.configuration.encoding
|
15
18
|
end
|
16
19
|
|
20
|
+
# Return encrypted string according to specified format.
|
21
|
+
# Return nil if field is whitespace.
|
17
22
|
def encrypt(field)
|
18
23
|
return if field !~ /\S/
|
19
24
|
aes = OpenSSL::Cipher.new(symmetric_algorithm)
|
20
25
|
aes_key = aes.random_key
|
21
26
|
iv = aes.random_iv
|
22
|
-
encrypt_data(iv, aes_key, field)
|
27
|
+
encrypt_data(iv, aes_key, field.force_encoding(encoding))
|
23
28
|
end
|
24
29
|
|
30
|
+
# Allow any field delimiter except a base64 character.
|
25
31
|
def field_delimiter=(delimiter)
|
26
32
|
if delimiter =~ /[A-Za-z0-9\/+]/
|
27
33
|
raise "This is not a valid delimiter! Must not be a character in Base64."
|
@@ -44,6 +50,8 @@ module Serket
|
|
44
50
|
parse(Base64.encode64(iv), Base64.encode64(encrypted_aes_key), Base64.encode64(encrypted_text))
|
45
51
|
end
|
46
52
|
|
53
|
+
# Format the final encrypted string to be returned depending
|
54
|
+
# on specified format.
|
47
55
|
def parse(iv, encrypted_key, encrypted_text)
|
48
56
|
case @format
|
49
57
|
when :delimited
|
data/lib/serket/version.rb
CHANGED
@@ -34,4 +34,24 @@ describe Serket::FieldEncrypter do
|
|
34
34
|
@parsed_json.has_key?('message').should be_true
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
describe "encoding" do
|
39
|
+
it "should encrypt and decrypt an accent character" do
|
40
|
+
characters = %w(á é í ó ú ü ñ ¿ ¡)
|
41
|
+
characters.each do |c|
|
42
|
+
encrypted = @field_encrypter.encrypt(c)
|
43
|
+
decrypted = @field_decrypter.decrypt(encrypted)
|
44
|
+
c.should == decrypted
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should encrypt and decrypt khmer" do
|
49
|
+
characters = %w(ឃ ង ទ ម វ ស )
|
50
|
+
characters.each do |c|
|
51
|
+
encrypted = @field_encrypter.encrypt(c)
|
52
|
+
decrypted = @field_decrypter.decrypt(encrypted)
|
53
|
+
c.should == decrypted
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
37
57
|
end
|