serket 0.1.2 → 0.2.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
  SHA1:
3
- metadata.gz: 5ba69de85b66353c39bb9ab36bbe137c03096ace
4
- data.tar.gz: 9992acd318cbc1aa4d739fcc6224f1bd71189314
3
+ metadata.gz: 9add635d8b06b59c7bc47526c0b22b90b9bedd17
4
+ data.tar.gz: b357446a204828c351bee22546f4e4f1365d8fe6
5
5
  SHA512:
6
- metadata.gz: c8e7853161ae2026ec1eb2051000263c0da21ec2e171d01fde51d60900ce64c22c5c32fc83c66725c74ad873a8df79bcf0d401b40b71aebe6985a8104355678c
7
- data.tar.gz: d01c0d8e9154170783a00cf2bccac560fdde8b4703428de75a7b26a1cc6be370ddbaf6215b4c5d1c151cc244fea4ecaf3d6382eb7011a85479247e1f51030dcf
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
@@ -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 = Serket.configuration.public_key_path
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
@@ -1,3 +1,3 @@
1
1
  module Serket
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Nipper