既知のダイジェストからではなく、既知の状態から。純粋なPythonMD5実装を使用して、その状態を保存できます。PyPyの_md5.pyを使用した例を次に示します。
import _md5
def md5_getstate(md):
return (md.A, md.B, md.C, md.D, md.count + [], md.input + [], md.length)
def md5_continue(state):
md = _md5.new()
(md.A, md.B, md.C, md.D, md.count, md.input, md.length) = state
return md
m1 = _md5.new()
m1.update("hello, ")
state = md5_getstate(m1)
m2 = md5_continue(state)
m2.update("world!")
print m2.hexdigest()
m = _md5.new()
m.update("hello, world!")
print m.hexdigest()
e.danが指摘したように、ほとんどすべてのチェックサムアルゴリズム(CRC、Adler、Fletcher)を使用することもできますが、意図的なデータ変更からは保護されず、ランダムエラーからのみ保護されます。
編集:もちろん、参照したスレッドのctypesを使用して、より移植性の高い方法で(マジック定数なしで)シリアル化メソッドを再実装することもできます。これはバージョン/アーキテクチャに依存しないはずだと思います(Python 2.4-2.7、i386とx86_64の両方でテスト済み):
# based on idea from http://groups.google.com/group/comp.lang.python/msg/b1c5bb87a3ff5e34
try:
import _md5 as md5
except ImportError:
# python 2.4
import md5
import ctypes
def md5_getstate(md):
if type(md) is not md5.MD5Type:
raise TypeError, 'not an MD5Type instance'
return ctypes.string_at(id(md) + object.__basicsize__,
md5.MD5Type.__basicsize__ - object.__basicsize__)
def md5_continue(state):
md = md5.new()
assert len(state) == md5.MD5Type.__basicsize__ - object.__basicsize__, \
'invalid state'
ctypes.memmove(id(md) + object.__basicsize__,
ctypes.c_char_p(state),
len(state))
return md
m1 = md5.new()
m1.update("hello, ")
state = md5_getstate(m1)
m2 = md5_continue(state)
m2.update("world!")
print m2.hexdigest()
m = md5.new()
m.update("hello, world!")
print m.hexdigest()
_md5 / md5モジュールがないため、Python3とは互換性がありません。
残念ながら、hashlibのopenssl_md5実装は、OpenSSL EVP APIがEVP_MD_CTXオブジェクトを確実にシリアル化するための呼び出し/メソッドを提供しないため、このようなハッキングには適していません。