Created
April 2, 2012 04:22
-
-
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
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
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