Created
June 16, 2011 21:07
-
-
Save pftg/1030279 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var setCryptData = function (data) { | |
window.cryptData = data.crypto_key; | |
}; | |
var submitFormWithCrypto = function (form, callback) { | |
$.getScript('/profiles/crypto_key?callback=setCryptData', function () { | |
var queryData = { | |
key_id: cryptData.id, | |
encrypted: teaEncrypt($(form).serialize(), cryptData.key) | |
}; | |
$.post(form.action.replace(/^https?:/, window.location.protocol), $.param(queryData), callback, "json"); | |
}); | |
}; | |
$('form#global-login-form').submit(function(e) { | |
e.preventDefault(); | |
submitFormWithCrypto(this, function(response) { | |
if (response.errors) { | |
$globalNav.find('div.sign-in div.alert-row').html(response.errors.join(" ")).slideDown(250); | |
} else { | |
insertUserMenu(response.menu); | |
$.publish("user-info-available", response.user); | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ApplicationController < ActionController::Base | |
before_filter :decrypt_params | |
def ssl_required? | |
use_ssl? && (request.ssl? || !request.xhr?) && super | |
end | |
def decrypt_params | |
if params[:key_id] && params[:encrypted] | |
key = CryptoKey.search_and_destroy(params.delete(:key_id)) | |
if key.nil? | |
render :nothing => true, :status => 401 | |
else | |
query_string = Crypt::BlockTea.decrypt(params.delete(:encrypted), key) | |
unencrypted_params = Rack::Utils.parse_nested_query(query_string) | |
params.merge!(unencrypted_params) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Crypt | |
module BlockTea | |
# | |
# encrypt: Use Corrected Block TEA to encrypt plaintext using password | |
# Return encrypted text as string | |
# | |
def encrypt(plaintext, password) | |
return '' if plaintext.blank? # nothing to encrypt | |
# 'escape' plaintext so chars outside ISO-8859-1 work in single-byte packing, but | |
# keep spaces as spaces (not '%20') so encrypted text doesn't grow too long, and | |
# convert result to longs | |
# v = strToLongs(escape(plaintext).gsub(/%20/,' ')) | |
v = string_to_longs(plaintext) | |
if v.length == 1 | |
v[1] = 0 # algorithm doesn't work for n<2 so fudge by adding nulls | |
end | |
k = string_to_longs(password.ljust(16).slice(0, 16)) # simply convert first 16 chars of password as key | |
n = v.length | |
z = v[n-1] | |
y = v[0] | |
delta = 0x9E3779B9 | |
#mx, e | |
sum = 0 | |
(6 + 52.0/n).floor.downto(1) { |q| # 6 + 52/n operations gives between 6 & 32 mixes on each word | |
sum = (sum + delta) & 0xffffffff | |
e = sum>>2 & 3 | |
for p in (0...n-1) | |
y = v[p+1] | |
mx = ((z>>5 ^ ((y<<2)&0xffffffff)) + (y>>3 ^ ((z<<4)&0xffffffff)) ^ (sum^y) + (k[p&3 ^ e] ^ z)) & 0xffffffff | |
v[p] = (v[p] + mx) & 0xffffffff | |
z = v[p] | |
end | |
y = v[0] | |
mx = ((z>>5 ^ ((y<<2)&0xffffffff)) + (y>>3 ^ ((z<<4)&0xffffffff)) ^ (sum^y) + (k[(n-1)&3 ^ e] ^ z)) & 0xffffffff | |
v[n-1] = (v[n-1] + mx) & 0xffffffff | |
z = v[n-1] | |
} | |
ciphertext = longs_to_string(v) | |
ciphertext.unpack('a*').pack('m').delete("\n") # base64 encode it without newlines | |
end | |
# | |
# decrypt: Use Corrected Block TEA to decrypt ciphertext using password | |
# | |
def decrypt(ciphertext, password) | |
return '' if ciphertext.blank? | |
v = string_to_longs(ciphertext.unpack('m').pack("a*")) # base64 decode and convert to array of 'longs' | |
k = string_to_longs(password.ljust(16).slice(0, 16)) | |
n = v.length | |
z = v[n-1] | |
y = v[0] | |
delta = 0x9E3779B9 | |
#mx, e | |
q = (6 + 52.0/n).floor | |
sum = q*delta | |
while (sum > 0) | |
e = sum>>2 & 3 | |
(n-1).downto(1) { |p| | |
z = v[p-1] | |
mx = ((z>>5 ^ ((y<<2)&0xffffffff)) + (y>>3 ^ ((z<<4)&0xffffffff)) ^ (sum^y) + (k[p&3 ^ e] ^ z)) & 0xffffffff | |
v[p] = (v[p] - mx) & 0xffffffff | |
y = v[p] | |
} | |
z = v[n-1] | |
mx = ((z>>5 ^ ((y<<2)&0xffffffff)) + (y>>3 ^ ((z<<4)&0xffffffff)) ^ (sum^y) + (k[0 ^ e] ^ z)) & 0xffffffff | |
v[0] = (v[0] - mx) & 0xffffffff | |
y = v[0] | |
sum -= delta | |
end | |
plaintext = longs_to_string(v) | |
# strip trailing null chars resulting from filling 4-char blocks: | |
plaintext.gsub(/\0+$/, '') | |
end | |
# supporting functions | |
def string_to_longs(s) | |
s = s.dup | |
s << [0, 0, 0].pack('c*') # Pad with at most three nulls | |
s.unpack('L*') | |
end | |
def longs_to_string(l) # convert array of longs back to string | |
l.pack('L*') | |
end | |
module_function :encrypt, :decrypt, :string_to_longs, :longs_to_string | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CryptoKey < ActiveRecord::Base | |
validates_presence_of :key | |
before_validation_on_create :set_key | |
def self.search_and_destroy(id) | |
key = self.find_by_id(id) | |
key.tap(&:destroy).key if key.present? | |
end | |
private | |
def set_key | |
self.key = ActiveSupport::SecureRandom.hex(32) if self.key.blank? | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CryptoKeysController < ApplicationController | |
ssl_required :show | |
def show | |
render :text => "#{params[:callback]}(#{CryptoKey.create.to_json});" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TEAencrypt: Use Corrected Block TEA to encrypt plaintext using password | |
// (note plaintext & password must be strings not string objects) | |
// | |
// Return encrypted text as string | |
// | |
function teaEncrypt(plaintext, password) | |
{ | |
if (plaintext.length == 0) return(''); // nothing to encrypt | |
// 'escape' plaintext so chars outside ISO-8859-1 work in single-byte packing, but | |
// keep spaces as spaces (not '%20') so encrypted text doesn't grow too long, and | |
// convert result to longs | |
// var v = strToLongs(escape(plaintext).replace(/%20/g,' ')); | |
var v = stringToLongs(plaintext); | |
if (v.length == 1) v[1] = 0; // algorithm doesn't work for n<2 so fudge by adding nulls | |
var k = stringToLongs(password.pad(16, ' ', 1).slice(0,16)); // simply convert first 16 chars of password as key | |
var n = v.length; | |
var z = v[n-1], y = v[0], delta = 0x9E3779B9; | |
var mx, e, q = Math.floor(6 + 52/n), sum = 0; | |
while (q-- > 0) { // 6 + 52/n operations gives between 6 & 32 mixes on each word | |
sum += delta; | |
e = sum>>>2 & 3; | |
for (var p = 0; p < n-1; p++) { | |
y = v[p+1]; | |
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z); | |
z = v[p] += mx; | |
} | |
y = v[0]; | |
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z); | |
z = v[n-1] += mx; | |
} | |
// note use of >>> in place of >> due to lack of 'unsigned' type in JavaScript | |
var ciphertext = longsToString(v); | |
return encode64(ciphertext); | |
} | |
// supporting functions | |
function stringToLongs(s) { // convert string to array of longs, each containing 4 chars | |
// note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long | |
var l = new Array(Math.ceil(s.length/4)); | |
for (var i=0; i<l.length; i++) { | |
// note little-endian encoding - endianness is irrelevant as long as | |
// it is the same in longsToStr() | |
l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) + | |
(s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24); | |
} | |
return l; // note running off the end of the string generates nulls since | |
} // bitwise operators treat NaN as 0 | |
function longsToString(l) { // convert array of longs back to string | |
var a = new Array(l.length); | |
for (var i=0; i<l.length; i++) { | |
a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF, | |
l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF); | |
} | |
return a.join(''); // use Array.join() rather than repeated string appends for efficiency | |
} | |
var keyStr = "ABCDEFGHIJKLMNOP" + | |
"QRSTUVWXYZabcdef" + | |
"ghijklmnopqrstuv" + | |
"wxyz0123456789+/" + | |
"="; | |
function encode64(input) { | |
var output = ""; | |
var chr1, chr2, chr3 = ""; | |
var enc1, enc2, enc3, enc4 = ""; | |
var i = 0; | |
do { | |
chr1 = input.charCodeAt(i++); | |
chr2 = input.charCodeAt(i++); | |
chr3 = input.charCodeAt(i++); | |
enc1 = chr1 >> 2; | |
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | |
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | |
enc4 = chr3 & 63; | |
if (isNaN(chr2)) { | |
enc3 = enc4 = 64; | |
} else if (isNaN(chr3)) { | |
enc4 = 64; | |
} | |
output = output + | |
keyStr.charAt(enc1) + | |
keyStr.charAt(enc2) + | |
keyStr.charAt(enc3) + | |
keyStr.charAt(enc4); | |
chr1 = chr2 = chr3 = ""; | |
enc1 = enc2 = enc3 = enc4 = ""; | |
} while (i < input.length); | |
return output; | |
} | |
function decode64(input) { | |
var output = ""; | |
var chr1, chr2, chr3 = ""; | |
var enc1, enc2, enc3, enc4 = ""; | |
var i = 0; | |
// remove all characters that are not A-Z, a-z, 0-9, +, /, or = | |
var base64test = /[^A-Za-z0-9\+\/\=]/g; | |
if (base64test.exec(input)) { | |
alert("There were invalid base64 characters in the input text.\n" + | |
"Valid base64 characters are A-Z, a-z, 0-9, '+', '/', and '='\n" + | |
"Expect errors in decoding.\n\n" + | |
'\'' + input + '\''); | |
} | |
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | |
do { | |
enc1 = keyStr.indexOf(input.charAt(i++)); | |
enc2 = keyStr.indexOf(input.charAt(i++)); | |
enc3 = keyStr.indexOf(input.charAt(i++)); | |
enc4 = keyStr.indexOf(input.charAt(i++)); | |
chr1 = (enc1 << 2) | (enc2 >> 4); | |
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); | |
chr3 = ((enc3 & 3) << 6) | enc4; | |
output = output + String.fromCharCode(chr1); | |
if (enc3 != 64) { | |
output = output + String.fromCharCode(chr2); | |
} | |
if (enc4 != 64) { | |
output = output + String.fromCharCode(chr3); | |
} | |
chr1 = chr2 = chr3 = ""; | |
enc1 = enc2 = enc3 = enc4 = ""; | |
} while (i < input.length); | |
return output; | |
} | |
String.prototype.pad = function(l, s, t){ //v1.0 | |
return s || (s = " "), (l -= this.length) > 0 ? (s = new Array(Math.ceil(l / s.length) | |
+ 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2)) | |
+ this + s.substr(0, l - t) : this; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment