2011-11-12 14 views
5

सोचा कि मैं खुद को हास्केल में SHA1 लागू करने का प्रयास करूंगा। मैं एक कार्यान्वयन के साथ आया जो शून्य स्ट्रिंग ("") के लिए सही जवाब संकलित करता है और लौटाता है, लेकिन कुछ भी नहीं। मैं यह नहीं समझ सकता कि क्या गलत हो सकता है। क्या एल्गोरिदम और SHA1 से परिचित कोई इसे इंगित कर सकता है?हास्केल में SHA1 - मेरे कार्यान्वयन के साथ कुछ गलत

import Data.Bits 
import Data.Int 
import Data.List 
import Data.Word 
import Text.Printf 
import qualified Data.ByteString.Lazy as L 
import qualified Data.ByteString.Lazy.Char8 as C 

h0 = 0x67452301 :: Word32 
h1 = 0xEFCDAB89 :: Word32 
h2 = 0x98BADCFE :: Word32 
h3 = 0x10325476 :: Word32 
h4 = 0xC3D2E1F0 :: Word32 

sha1string :: String -> String 
sha1string s = concat $ map (printf "%02x") $ sha1 . C.pack $ s 

sha1 :: L.ByteString -> [Word8] 
sha1 msg = concat [w32ToComps a, w32ToComps b, w32ToComps c, w32ToComps d, w32ToComps e] 
    where (a, b, c, d, e) = sha1' msg 0 h0 h1 h2 h3 h4 

sha1' msg sz a b c d e 
    | L.length m1 < 64 = sha1'last (padded msg sz) a b c d e 
    | otherwise  = uncurry5 (sha1' m2 (sz + 64)) $ whole a b c d e m1 
    where (m1, m2) = L.splitAt 64 msg 

sha1'last msg a b c d e 
    | m1 == L.empty = (a, b, c, d, e) 
    | otherwise  = uncurry5 (sha1'last m2) $ whole a b c d e m1 
    where (m1, m2) = L.splitAt 64 msg 

whole a b c d e msg = partcd (partab msg) a b c d e 

partcd ws a b c d e = (h0 + a', h1 + b', h2 + c', h3 + d', h4 + e') 
    where 
    (a', b', c', d', e') = go ws a b c d e 0 
    go ws a b c d e 80 = (a, b, c, d, e) 
    go (w:ws) a b c d e t = go ws temp a (rotate b 30) c d (t+1) 
     where temp = (rotate a 5) + f t b c d + e + w + k t 

partab chunk = take 80 ns 
    where 
    ns  = initial ++ zipWith4 g (drop 13 ns) (drop 8 ns) (drop 2 ns) ns 
    g a b c d = rotate (a `xor` b `xor` c `xor` d) 1 
    initial = map (L.foldl (\a b -> (a * 256) + fromIntegral b) 0) $ paginate 4 chunk 

f t b c d 
    | t >= 0 && t <= 19 = (b .&. c) .|. ((complement b) .&. d) 
    | t >= 20 && t <= 39 = b `xor` c `xor` d 
    | t >= 40 && t <= 59 = (b .&. c) .|. (b .&. d) .|. (c .&. d) 
    | t >= 60 && t <= 79 = b `xor` c `xor` d 

k t 
    | t >= 0 && t <= 19 = 0x5A827999 
    | t >= 20 && t <= 39 = 0x6ED9EBA1 
    | t >= 40 && t <= 59 = 0x8F1BBCDC 
    | t >= 60 && t <= 79 = 0xCA62C1D6 

padded msg prevsz = L.append msg (L.pack pad) 
    where 
    sz  = L.length msg 
    totalsz = prevsz + sz 
    padsz = fromIntegral $ (128 - 9 - sz) `mod` 64 
    pad  = [0x80] ++ (replicate padsz 0) ++ int64ToComps totalsz 

uncurry5 f (a, b, c, d, e) = f a b c d e 

paginate n xs 
    | xs == L.empty = [] 
    | otherwise  = let (a, b) = L.splitAt n xs in a : paginate n b 

w32ToComps :: Word32 -> [Word8] 
w32ToComps = integerToComps [24, 16 .. 0] 

int64ToComps :: Int64 -> [Word8] 
int64ToComps = integerToComps [56, 48 .. 0] 

integerToComps :: (Integral a, Bits a) => [Int] -> a -> [Word8] 
integerToComps bits x = map f bits 
    where f n = fromIntegral ((x `shiftR` n) .&. 0xff) :: Word8 
+0

डीबगिंग करते समय, यह बहुत मददगार है यदि आप कॉल स्टैक में सबसे गहन कार्य को समस्या को कम कर सकते हैं जो कुछ अप्रत्याशित करता है। क्या आप ghci में अन्य कार्यों में कुछ कॉल करने का प्रयास कर सकते हैं और यह सत्यापित कर सकते हैं कि वे गणना कर रहे हैं कि आप उनकी गणना करने की अपेक्षा करते हैं? –

उत्तर

9

शुरुआत के लिए, आप बाइट (sz + 64 देखें) में एक आकार गिनती रखने होना दिखाई देते हैं, लेकिन गिनती संलग्न हो जाता है कि बिट्स में होना चाहिए ताकि आप 8 कहीं (संयोग से गुणा करने की आवश्यकता है, मैं आप का उपयोग करने का सुझाव cereal या binary अपने एंड इंटीजर को बड़े एंडियन वर्ड 64 में घुमाने के बजाय)। यद्यपि यह एकमात्र समस्या नहीं है।

संपादित करें: मिला यह

आह-हा! कभी नहीं भूलें, विकिपीडिया अनिवार्य, परिवर्तनीय-दुनिया के अनजानों के समूह द्वारा लिखी गई है! आप h0 + a', h1 + b', ... के साथ प्रत्येक खंड को खत्म करते हैं लेकिन यह पुराना संदर्भ और आपके नए मान होना चाहिए: a + a', b + b', ...। सब कुछ उसके बाद (और उपरोक्त आकार) ठीक हो जाता है।

परीक्षण कोड अब 5 संपत्ति परीक्षणों और 12 9 केएटी के साथ पूरा हो गया है।

समाप्ति संपादित

यह आप बाहर बहुत मदद करता है, तो आप सामान्य प्रारंभिक, अद्यतन में अपने कार्यान्वयन विभाजित, संचालन को अंतिम रूप देने होगा। इस तरह आप अन्य कार्यान्वयन के साथ मध्यवर्ती परिणामों की तुलना कर सकते हैं।

मैंने अभी आपके कार्यान्वयन के लिए crypto-api-tests का उपयोग करके परीक्षण कोड बनाया है। यदि आप रुचि रखते हैं तो अतिरिक्त कोड नीचे दिया गया है, crypto-api-tests इंस्टॉल करना न भूलें।

import Test.SHA 
import Test.Crypto 
import Crypto.Classes 
import Data.Serialize 
import Data.Tagged 
import Control.Monad 

main = defaultMain =<< makeSHA1Tests (undefined :: SHA1) 

data SHA1 = SHA1 [Word8] 
    deriving (Eq, Ord, Show) 
data CTX = CTX L.ByteString 
instance Serialize SHA1 where 
    get = liftM SHA1 (mapM (const get) [1..20]) 
    put (SHA1 x) = mapM_ put x 

instance Hash CTX SHA1 where 
    outputLength = Tagged 160 
    blockLength = Tagged (64*8) 
    initialCtx = CTX L.empty 
    updateCtx (CTX m) x = CTX (L.append m (L.fromChunks [x])) 
    finalize (CTX m) b = SHA1 $ sha1 (L.append m (L.fromChunks [b])) 
+0

यह अविश्वसनीय है, थॉमस। आपका बहुत बहुत धन्यवाद :) – Ana

संबंधित मुद्दे