urlcrypt 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +62 -22
- data/Rakefile +1 -1
- data/lib/URLcrypt.rb +34 -4
- data/test/URLcrypt_test.rb +19 -2
- metadata +3 -3
data/README.md
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
# URLcrypt
|
2
2
|
|
3
|
+
[![Build Status](https://travis-ci.org/madrobby/URLcrypt.png?branch=master)](https://travis-ci.org/madrobby/URLcrypt)
|
4
|
+
|
3
5
|
Ever wanted to securely transmit (not too long) pieces of arbitrary binary data
|
4
|
-
in a URL?
|
6
|
+
in a URL? **URLcrypt** makes it easy!
|
5
7
|
|
6
|
-
This gem is based on the base32 gem from Samuel Tesla.
|
8
|
+
This gem is based on the [base32](https://github.com/stesla/base32) gem from Samuel Tesla.
|
7
9
|
|
8
|
-
URLcrypt uses
|
9
|
-
and encodes and decodes
|
10
|
+
URLcrypt uses **256-bit AES symmetric encryption**
|
11
|
+
to securely encrypt data, and encodes and decodes
|
12
|
+
**Base 32 strings that can be used directly in URLs**.
|
10
13
|
|
11
|
-
|
12
|
-
access a web application from a place
|
13
|
-
|
14
|
+
This can be used to securely store user ids, download expiration dates and
|
15
|
+
other arbitrary data like that when you access a web application from a place
|
16
|
+
that doesn't have other authentication or persistence mechanisms (like cookies):
|
17
|
+
|
18
|
+
* Loading a generated image from your web app in an email
|
19
|
+
* Links that come with an expiration date (à la S3)
|
20
|
+
* Mini-apps that don't persist data on the server
|
14
21
|
|
15
|
-
|
16
|
-
and doesn't use vowels to avoid bad words in the generated string.
|
17
|
-
|
18
|
-
The main reason why Base 32 is useful is that Ruby's built-in Base 64 support
|
19
|
-
is, IMO, looking ugly in URLs and requires several characters that need to be
|
20
|
-
URL-escaped.
|
21
|
-
|
22
|
-
Unfortunately, some other gems out there that in theory could handle this
|
23
|
-
(like the radix gem) fail with strings that start with a "\0" byte.
|
22
|
+
Works with Ruby 1.8, 1.9 and 2.0.
|
24
23
|
|
25
|
-
|
26
|
-
|
24
|
+
**Important**: As a general guideline, URL lengths shouldn't exceed about 2000
|
25
|
+
characters in length, as URLs longer than that will not work in some browsers
|
26
|
+
and with some (proxy) servers. This limits the amount of data you can store
|
27
|
+
with URLcrypt.
|
27
28
|
|
28
|
-
|
29
|
-
just yet. It will only work on Ruby 1.8.7 for now.
|
29
|
+
**WORD OF WARNING: THERE IS NO GUARANTEE WHATSOEVER THAT THIS GEM IS ACTUALLY SECURE AND WORKS. USE AT YOUR OWN RISK.**
|
30
30
|
|
31
|
-
Patches are welcome
|
31
|
+
Patches are welcome; please include tests!
|
32
32
|
|
33
33
|
## Installation
|
34
34
|
|
@@ -37,16 +37,56 @@ Add the `urlcrypt` gem to your Gemfile.
|
|
37
37
|
## Simple Example
|
38
38
|
|
39
39
|
```ruby
|
40
|
+
# encrypt and encode with 256-bit AES
|
41
|
+
# one-time setup, set this to a securely random key with at least 256 bits, see below
|
42
|
+
URLcrypt::key = '...'
|
43
|
+
|
44
|
+
# now encrypt and decrypt!
|
45
|
+
URLcrypt::encrypt('chunky bacon!') # => "sgmt40kbmnh1663nvwknxk5l0mZ6Av2ndhgw80rkypnp17xmmg5hy"
|
46
|
+
URLcrypt::decrypt('sgmt40kbmnh1663nvwknxk5l0mZ6Av2ndhgw80rkypnp17xmmg5hy')
|
47
|
+
# => "chunky bacon!"
|
48
|
+
|
49
|
+
# encoding without encryption (don't use for anything sensitive!), doesn't need key set
|
40
50
|
URLcrypt.encode('chunky bacon!') # => "mnAhk6tlp2qg2yldn8xcc"
|
41
51
|
URLcrypt.decode('mnAhk6tlp2qg2yldn8xcc') # => "chunky bacon!"
|
42
52
|
```
|
43
53
|
|
54
|
+
### Generating keys
|
55
|
+
|
56
|
+
The easiest way to generate a secure key is to use `rake secret` in a Rails app:
|
57
|
+
|
58
|
+
```
|
59
|
+
% rake secret
|
60
|
+
ba7f56f8f9873b1653d7f032cc474938fd749ee8fbbf731a7c41d698826aca3cebfffa832be7e6bc16eaddc3826602f35d3fd6b185f261ee8b0f01d33adfbe64
|
61
|
+
```
|
62
|
+
|
63
|
+
To use the key with URLcrypt, you'll need to convert that from a hex string into a real byte array:
|
64
|
+
|
65
|
+
```
|
66
|
+
URLcrypt::key = ['longhexkeygoeshere'].pack('H*')
|
67
|
+
```
|
68
|
+
|
44
69
|
## Running the Test Suite
|
45
70
|
|
46
71
|
If you want to run the automated tests for URLcrypt, issue this command from the
|
47
72
|
distribution directory.
|
48
73
|
|
49
|
-
|
74
|
+
```
|
75
|
+
% rake test:all
|
76
|
+
```
|
77
|
+
|
78
|
+
## Why not Base 64, or an other radix/base library?
|
79
|
+
|
80
|
+
URLcrypt uses a modified Base 32 algorithm that doesn't use padding characters,
|
81
|
+
and doesn't use vowels to avoid bad words in the generated string.
|
82
|
+
|
83
|
+
The main reason why Base 32 is useful is that Ruby's built-in Base 64 support
|
84
|
+
is, IMO, looking ugly in URLs and requires several characters that need to be
|
85
|
+
URL-escaped.
|
86
|
+
|
87
|
+
Unfortunately, some other gems out there that in theory could handle this
|
88
|
+
(like the radix gem) fail with strings that start with a "\0" byte.
|
89
|
+
|
50
90
|
|
51
91
|
## References
|
52
92
|
|
data/Rakefile
CHANGED
@@ -37,7 +37,7 @@ gemspec = Gem::Specification.new do |s|
|
|
37
37
|
s.require_paths << 'lib'
|
38
38
|
s.requirements << 'none'
|
39
39
|
s.summary = "Securely encode and decode short pieces of arbitrary binary data in URLs."
|
40
|
-
s.version = "0.0
|
40
|
+
s.version = "0.1.0"
|
41
41
|
end
|
42
42
|
|
43
43
|
Gem::PackageTask.new(gemspec) do |pkg|
|
data/lib/URLcrypt.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
1
3
|
module URLcrypt
|
2
4
|
# avoid vowels to not generate four-letter words, etc.
|
3
5
|
# this is important because those words can trigger spam
|
4
6
|
# filters when URLs are used in emails
|
5
7
|
TABLE = "1bcd2fgh3jklmn4pqrstAvwxyz567890".freeze
|
6
8
|
|
9
|
+
def self.key=(key)
|
10
|
+
@key = key
|
11
|
+
end
|
12
|
+
|
7
13
|
class Chunk
|
8
14
|
def initialize(bytes)
|
9
15
|
@bytes = bytes
|
@@ -25,6 +31,7 @@ module URLcrypt
|
|
25
31
|
[(0..n-1).to_a.reverse.collect {|i| TABLE[(c >> i * 5) & 0x1f].chr},
|
26
32
|
("=" * (8-n))] # TODO: remove '=' padding generation
|
27
33
|
end
|
34
|
+
|
28
35
|
end
|
29
36
|
|
30
37
|
def self.chunks(str, size)
|
@@ -38,11 +45,34 @@ module URLcrypt
|
|
38
45
|
end
|
39
46
|
|
40
47
|
# strip '=' padding, because we don't need it
|
41
|
-
def self.encode(
|
42
|
-
chunks(
|
48
|
+
def self.encode(data)
|
49
|
+
chunks(data, 5).collect(&:encode).flatten.join.tr('=','')
|
43
50
|
end
|
44
51
|
|
45
|
-
def self.decode(
|
46
|
-
chunks(
|
52
|
+
def self.decode(data)
|
53
|
+
chunks(data, 8).collect(&:decode).flatten.join
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.decrypt(data)
|
57
|
+
parts = data.split('Z').map{|part| decode(part)}
|
58
|
+
decrypter = cipher(:decrypt)
|
59
|
+
decrypter.iv = parts[0]
|
60
|
+
decrypter.update(parts[1]) + decrypter.final
|
47
61
|
end
|
62
|
+
|
63
|
+
def self.encrypt(data)
|
64
|
+
crypter = cipher(:encrypt)
|
65
|
+
crypter.iv = iv = crypter.random_iv
|
66
|
+
"#{encode(iv)}Z#{encode(crypter.update(data) + crypter.final)}"
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def self.cipher(mode)
|
72
|
+
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
73
|
+
cipher.send(mode)
|
74
|
+
cipher.key = @key
|
75
|
+
cipher
|
76
|
+
end
|
77
|
+
|
48
78
|
end
|
data/test/URLcrypt_test.rb
CHANGED
@@ -1,15 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'test/unit'
|
2
3
|
require 'URLcrypt'
|
3
4
|
|
4
5
|
class TestURLcrypt < Test::Unit::TestCase
|
6
|
+
def assert_bytes_equal(string1, string2)
|
7
|
+
bytes1 = string1.bytes.to_a.join(':')
|
8
|
+
bytes2 = string2.bytes.to_a.join(':')
|
9
|
+
assert_equal(bytes1, bytes2)
|
10
|
+
end
|
11
|
+
|
5
12
|
def assert_decoding(encoded, plain)
|
6
13
|
decoded = URLcrypt.decode(encoded)
|
7
|
-
|
14
|
+
assert_bytes_equal(plain, decoded)
|
8
15
|
end
|
9
16
|
|
10
17
|
def assert_encoding(encoded, plain)
|
11
18
|
actual = URLcrypt.encode(plain)
|
12
|
-
|
19
|
+
assert_bytes_equal(encoded, actual)
|
13
20
|
end
|
14
21
|
|
15
22
|
def assert_encode_and_decode(encoded, plain)
|
@@ -38,5 +45,15 @@ class TestURLcrypt < Test::Unit::TestCase
|
|
38
45
|
assert_decoding(encoded, original)
|
39
46
|
end
|
40
47
|
end
|
48
|
+
|
49
|
+
def test_encryption
|
50
|
+
# this key was generated via rake secret in a rails app, the pack() converts it into a byte array
|
51
|
+
URLcrypt::key =
|
52
|
+
['d25883a27b9a639da85ea7e159b661218799c9efa63069fac13a6778c954fb6d721968887a19bdb01af8f59eb5a90d256bd9903355c20b0b4b39bf4048b9b17b'].pack('H*')
|
53
|
+
|
54
|
+
original = "hello world!"
|
55
|
+
encrypted = URLcrypt::encrypt(original)
|
56
|
+
assert_equal(URLcrypt::decrypt(encrypted), original)
|
57
|
+
end
|
41
58
|
|
42
59
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: urlcrypt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Thomas Fuchs
|