netease-fm-1.2.2: lib/FM/NetEase/Crypto.hs
{-# LANGUAGE OverloadedStrings #-}
module FM.NetEase.Crypto (
toHex
, encryptAES
, encryptRSA
, encryptSongId
, encryptPassword
) where
import qualified Crypto.Error as C
import qualified Crypto.Cipher.AES as C
import qualified Crypto.Cipher.Types as C
import qualified Crypto.Hash as C
import Data.Bits (xor)
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import qualified Data.ByteString.Base64 as Base64
import Data.Char (chr, ord)
import Data.Maybe (fromJust)
import Numeric (showHex)
toHex :: (Integral a, Show a) => a -> BS.ByteString
toHex n = BS8.pack (showHex n [])
encryptAES :: BS.ByteString -> BS.ByteString -> BS.ByteString
encryptAES key text = encrypt key (encrypt ("0CoJUm6Qyw8W8jud" :: BS.ByteString) text)
where
encrypt key text = Base64.encode $ C.cbcEncrypt cipher iv plain
where
cipher = fromJust $ C.maybeCryptoError (C.cipherInit key) :: C.AES128
plain = text `mappend` BS.replicate k (toEnum k)
where k = 16 - BS.length text `mod` 16
iv = fromJust $ C.makeIV ("0102030405060708" :: BS.ByteString)
encryptRSA :: BS.ByteString -> BS.ByteString
encryptRSA text = zfill 256 $ toHex $ (base^publicKey) `mod` modulo
where
zfill n xs = BS.replicate (n - BS.length xs) (toEnum $ ord '0') `mappend` xs
base = foldr ((\x y -> y * 256 + x) . toInteger) 0 (BS.unpack text)
publicKey = 65537 :: Integer
modulo = read $ concat [ "1577947502671315022124768178003"
, "4549812187278333338974742401153"
, "1025366277535262539913701806290"
, "7664791894775335978549896068031"
, "9425397866032994198078607243280"
, "6427833685472618792592200595694"
, "3468729513017705807651353492595"
, "9016749053613808246968063851441"
, "6594216629258349130257685001248"
, "172188325316586707301643237607"
] :: Integer
encryptSongId :: String -> String
encryptSongId id = flip map hashValue $
\c -> case c of
'/' -> '_'
'+' -> '-'
c -> c
where
key = map ord "3go8&$8*3*3h0k(2)2"
bytes = zipWith xor (map ord id) (concat (repeat key))
hashValue = BS8.unpack $ Base64.encode $ BS.pack $ BA.unpack $ C.hashWith C.MD5 $ BS8.pack $ map chr bytes
encryptPassword :: String -> String
encryptPassword = show . C.hashWith C.MD5 . BS8.pack