packages feed

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