From: Brian Candler Date: 2003-08-07T19:38:47+09:00 Subject: Re: More on DRB & OpenSSL On Thu, Aug 07, 2003 at 06:49:32PM +0900, Hugh Sasse Staff Elec Eng wrote: > It doesn't encrypt the message, but does a checksum with data that > is never transmitted. Thus you can only forge the checksum if you > have that data, so you can trust it. From the comments I wrote: > > # A nonce is a word that is used only once (according to concise > # Oxford Dictionary.) The purpose is that it is generated, and a > # password is added to it, and the hash of the whole string is > # generated. Thus a hash is passed across the network so that the > # password can be checked against this hash without having to send > # the password across the network. I'm not sure why you want a nonce here; just a hash of (message + shared secret) will do. But if you're paranoid you'll sign your objects with a timestamp as well. Try the code below. You can store session objects in a HTML input field like this, or if the objects are small enough they can be sent to the browser as a cookie! Regards, Brian. class SecureMarshall def initialize(secret, lifetime = 3600) require 'digest/md5' unless secret.is_a? String and secret.size >= 12 raise "SecureMarshall secret must be at least 12 characters" end @secret = secret @lifetime = lifetime end def encode(obj) out = Marshal.dump([obj, Time.now.to_i + @lifetime]) [Marshal.dump([out, Digest::MD5::digest(out + @secret)])]. \ pack("m").gsub(/\n/,'') # base64 encode end def decode(key) raise TypeError, "Input must be a String" unless key.is_a? String out, hash = Marshal.load(key.unpack("m")[0]) if Digest::MD5::digest(out + @secret) != hash raise ArgumentError, "Invalid signature! Tampering with objects is forbidden" end obj, expiry = Marshal.load(out) if Time.now.to_i > expiry raise "Object expired" end obj end def freshen(key) encode(decode(key)) # re-sign the object with a new lifetime end end if __FILE__ == $0 require 'test/unit' class MarshallTest < Test::Unit::TestCase def setup @crypt = SecureMarshall.new("wibbly bibbly", 2) @obj = ["fred", "jim"] end def test_decode a = @crypt.encode(@obj) b = @crypt.decode(a) assert_equal(@obj,b) end def test_wrong_key a = @crypt.encode(@obj) crypt2 = SecureMarshall.new("another secret", 2) assert_raises(ArgumentError) { crypt2.decode(a) } end def test_tamper a = @crypt.encode(@obj) a[25] += 1 # in the middle of the MD5 checksum assert_raises(ArgumentError) { @crypt.decode(a) } end def test_lifetime a = @crypt.encode(@obj) sleep 4 assert_raises(RuntimeError) { @crypt.decode(a) } end def test_freshen a = @crypt.encode(@obj) 4.times do sleep 1 a = @crypt.freshen(a) end b = @crypt.decode(a) assert_equal(@obj,b) end end end