[ARVADOS] created: f569984ab4f48b393901fe3295218e576e81b9eb
git at public.curoverse.com
git at public.curoverse.com
Tue Mar 4 17:13:14 EST 2014
at f569984ab4f48b393901fe3295218e576e81b9eb (commit)
commit f569984ab4f48b393901fe3295218e576e81b9eb
Author: Tom Clegg <tom at curoverse.com>
Date: Tue Mar 4 17:12:25 2014 -0500
Add signing and verification code for blob permissions
diff --git a/services/api/app/models/blob.rb b/services/api/app/models/blob.rb
new file mode 100644
index 0000000..0ba299a
--- /dev/null
+++ b/services/api/app/models/blob.rb
@@ -0,0 +1,60 @@
+class Blob
+ class InvalidSignatureError < StandardError
+ end
+
+ def self.sign_locator blob_locator, opts
+ # We only use the hash portion for signatures.
+ blob_hash = blob_locator.split('+').first
+
+ # Generate an expiry timestamp (seconds since epoch, base 16)
+ timestamp = (Time.now.to_i + (opts[:ttl] || 600)).to_s(16)
+ # => "53163cb4"
+
+ # Generate a signature.
+ signature =
+ OpenSSL::HMAC.hexdigest('sha1', opts[:key],
+ [blob_hash,
+ opts[:api_token],
+ timestamp].join('@'))
+
+ blob_locator + '+A' + signature + '@' + timestamp
+ end
+
+ def self.verify_signature *args
+ begin
+ self.verify_signature! *args
+ true
+ rescue Blob::InvalidSignatureError
+ false
+ end
+ end
+
+ def self.verify_signature! signed_blob_locator, opts
+ blob_hash = signed_blob_locator.split('+').first
+ given_signature, timestamp = signed_blob_locator.
+ split('+A').last.
+ split('+').first.
+ split('@')
+
+ if !timestamp
+ raise Blob::InvalidSignatureError.new 'No signature provided.'
+ end
+ if !timestamp.match /^[\da-f]+$/
+ raise Blob::InvalidSignatureError.new 'Timestamp is not a base16 number.'
+ end
+ if timestamp.to_i(16) < Time.now.to_i
+ raise Blob::InvalidSignatureError.new 'Signature expiry time has passed.'
+ end
+
+ my_signature =
+ OpenSSL::HMAC.hexdigest('sha1', opts[:key],
+ [blob_hash,
+ opts[:api_token],
+ timestamp].join('@'))
+ if my_signature != given_signature
+ raise Blob::InvalidSignatureError.new 'Signature is invalid.'
+ end
+
+ true
+ end
+end
diff --git a/services/api/test/unit/blob_test.rb b/services/api/test/unit/blob_test.rb
new file mode 100644
index 0000000..ec6e67a
--- /dev/null
+++ b/services/api/test/unit/blob_test.rb
@@ -0,0 +1,94 @@
+require 'test_helper'
+
+class BlobTest < ActiveSupport::TestCase
+ @@api_token = rand(2**512).to_s(36)[0..49]
+ @@key = rand(2**2048).to_s(36)
+ @@blob_data = 'foo'
+ @@blob_locator = Digest::MD5.hexdigest(@@blob_data) +
+ '+' + @@blob_data.size.to_s
+
+ test 'correct' do
+ signed = Blob.sign_locator @@blob_locator, api_token: @@api_token, key: @@key
+ assert_equal true, Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+
+ test 'expired' do
+ signed = Blob.sign_locator @@blob_locator, api_token: @@api_token, key: @@key, ttl: -1
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'expired, but no raise' do
+ signed = Blob.sign_locator @@blob_locator, api_token: @@api_token, key: @@key, ttl: -1
+ assert_equal false, Blob.verify_signature(signed,
+ api_token: @@api_token,
+ key: @@key)
+ end
+
+ test 'bogus, wrong block hash' do
+ signed = Blob.sign_locator @@blob_locator, api_token: @@api_token, key: @@key
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed.sub('acbd','abcd'), api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, expired' do
+ signed = 'acbd18db4cc2f85cedef654fccc4a4d8+3+Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa at 531641bf'
+ assert_raises Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, wrong key' do
+ signed = Blob.sign_locator(@@blob_locator,
+ api_token: @@api_token,
+ key: (@@key+'x'))
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, wrong api token' do
+ signed = Blob.sign_locator(@@blob_locator,
+ api_token: @@api_token.reverse,
+ key: @@key)
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, signature format 1' do
+ signed = 'acbd18db4cc2f85cedef654fccc4a4d8+3+Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@'
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, signature format 2' do
+ signed = 'acbd18db4cc2f85cedef654fccc4a4d8+3+A at 531641bf'
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, signature format 3' do
+ signed = 'acbd18db4cc2f85cedef654fccc4a4d8+3+Axyzzy at 531641bf'
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'bogus, timestamp format' do
+ signed = 'acbd18db4cc2f85cedef654fccc4a4d8+3+Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa at xyzzy'
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(signed, api_token: @@api_token, key: @@key)
+ end
+ end
+
+ test 'no signature at all' do
+ assert_raise Blob::InvalidSignatureError do
+ Blob.verify_signature!(@@blob_locator, api_token: @@api_token, key: @@key)
+ end
+ end
+end
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list