Skip to content

Instantly share code, notes, and snippets.

@parameme
Created April 2, 2012 04:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parameme/2280705 to your computer and use it in GitHub Desktop.
Save parameme/2280705 to your computer and use it in GitHub Desktop.
Quick and dirty way to serialize and restore ruby Digest::SHA1 using FFI
require 'ffi'
require 'digest'
# Thanks to all ruby hackers everywhere who have donated their knowledge to the commons!
# With thanks to @judofyr for ffi/"evil.rb" - https://gist.github.com/2238438
class Object
def memory_location
object_id * 2
end
def to_pointer
FFI::Pointer.new(memory_location)
end
end
# With thanks to @neelance for ffi_gen - https://github.com/neelance/ffi_gen
class RData < FFI::Struct
layout :flags, :uint32,
:klass, :pointer,
:dmark, :pointer,
:dfree, :pointer,
:data, :pointer
end
class SHA1_CTX < FFI::Struct
layout :state, [:uint32,5],
:count, [:uint32,2],
:buffer, [:uint8,64]
end
if $0 == __FILE__
# Develop a test string
test_string = ""
128.times {|n| test_string << n.chr }
# Use native routines first
test_hexdigest = Digest::SHA1.hexdigest test_string
# Hash first 64 characters of test string
h = Digest::SHA1.new
# Now find the offset to its context (result of Data_Wrap_Struct so it is an RData)
hm = RData.new h.to_pointer
# For giggles, and verification of initial state - parse and display SHA1 context
# Should equal the hardcoded initial bytes from ../ruby/ext/digest/sha1.c
ctx = SHA1_CTX.new hm[:data]
ctx[:state].each {|e| puts e.to_s(16)}
ctx[:count].each {|e| puts e.to_s(16)}
puts ctx[:buffer].inspect
# Consume first half of test_string on digest "h"
h.update test_string[0,64]
# Save the "h" context into a String (binary)
serialized_context = hm[:data].get_bytes(0,SHA1_CTX.layout.size)
# Go off and do something here....
# Time passes...
# Need to continue input to digest
# Declare completely new hash
f = Digest::SHA1.new
# Determine new location
fm = RData.new f.to_pointer
# Ensure that we are not simply sharing the contexts
puts "hm[:data] != fm[:data] #=> #{hm[:data] != fm[:data]}"
# Restore "f" context from serialized form
fm[:data].put_bytes(0, serialized_context)
# Continue with remainder of test_string on digest "f"
f.update test_string[64,64]
# verify that we get the correct result after all input consumed
puts "test_hexdigest == f.hexdigest #=> #{test_hexdigest == f.hexdigest}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment