packages feed

cabal-sign (empty) → 0.1.0.0

raw patch · 4 files changed

+172/−0 lines, 4 filesdep +basedep +bytestringdep +cerealsetup-changed

Dependencies added: base, bytestring, cereal, directory, filepath, optparse-applicative, process, pureMD5, tar, zlib

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2013, Chris Done++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Chris Done nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cabal-sign.cabal view
@@ -0,0 +1,26 @@+name:                cabal-sign+version:             0.1.0.0+synopsis:            Sign and verify Cabal packages.+description:         Sign and verify Cabal packages.+license:             BSD3+license-file:        LICENSE+author:              Chris Done+maintainer:          chrisdone@gmail.com+category:            Development+build-type:          Simple+cabal-version:       >=1.8++executable cabal-sign+  main-is:             Main.hs+  ghc-options: -O2+  hs-source-dirs:      src+  build-depends:       base > 4 && < 5,+                       pureMD5,+                       optparse-applicative,+                       zlib,+                       tar,+                       bytestring,+                       directory,+                       cereal,+                       filepath,+                       process
+ src/Main.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import           Codec.Archive.Tar (Entry,EntryContent(..))+import qualified Codec.Archive.Tar as Tar+import qualified Codec.Archive.Tar.Entry as Tar+import qualified Codec.Compression.GZip as Gzip+import           Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as L+import qualified Data.Digest.Pure.MD5 as Md5+import           Data.List+import           Data.Serialize+import           Data.String+import           System.Directory+import           System.Environment+import           System.FilePath+import           System.IO+import           System.Process++data Options = OptionSign FilePath | Verify FilePath++main = do+  (cmd:archive:_) <- getArgs+  case cmd of+    "sign" -> sumAndSign archive+    "verify" -> verify archive++sumAndSign :: FilePath -> IO ()+sumAndSign fp = do+  exists <- doesFileExist fp+  if not exists+     then error $ fp ++ " doesn't exist"+     else do gzip <- L.readFile fp+             entries <- getGzipEntries gzip+             L.writeFile sum (checksum entries)+             rawSystem "gpg" ["--detach-sign",sum]+             removeFile sum+             addSignature fp (sum <.> "sig") entries++  where sum = translate ".sum" fp++addSignature :: FilePath -> FilePath -> [Entry] -> IO ()+addSignature gz sig entries = do+  signature <- L.readFile sig+  case Tar.toTarPath False (makeSigName gz) of+    Left err -> error err+    Right spath -> do+      let sigEntry = Tar.fileEntry spath signature+      L.writeFile (translate ".signed.tar.gz" gz)+                  (Gzip.compress (Tar.write (sigEntry : entries)))+      removeFile sig++makeSigName = translate ".sig" . takeFileName++translate ext = (++ ext) . dropSigned . dropExtension . dropExtension where+  dropSigned x | isSuffixOf ".signed" x = dropExtension x+               | otherwise = x+++getGzipEntries gzip =+  case result of+    Left err -> error ("tar reading error: " ++ show err)+    Right entries -> return entries++  where result = Tar.foldEntries (fmap . (:))+                                 (Right [])+                                 Left+                                 (Tar.read (Gzip.decompress gzip))++checksum :: [Entry] -> ByteString+checksum entries = L.intercalate "\n" (filter (not . L.null) (map hashEntry (sort (map Tar.entryContent entries))))++hashEntry :: EntryContent -> ByteString+hashEntry entry =+  case entry of+    NormalFile bytes _          -> hexify (Md5.md5 bytes)+    SymbolicLink target         -> encodeTarget target+    HardLink target             -> encodeTarget target+    OtherEntryType typ bytes _  -> hexify (Md5.md5 (L.cons (word typ) bytes))+    CharacterDevice major minor -> L.cons (word 'c') (L.concat [iword major,iword minor])+    BlockDevice major minor     -> L.cons (word 'b') (L.concat [iword major,iword minor])+    _ -> L.empty++  where encodeTarget = fromString . Tar.fromLinkTarget+        iword = fromString . show+        hexify = fromString . show+        word = fromIntegral . fromEnum++verify :: FilePath -> IO ()+verify fp = do+  exists <- doesFileExist fp+  if not exists+     then error $ fp ++ " doesn't exist"+     else do gzip <- L.readFile fp+             entries <- getGzipEntries gzip+             case find isSig entries of+               Nothing -> error $ "unable to find " ++ sigName ++ " in archive"+               Just entry -> do+                 L.writeFile sum (checksum (filter (not . isSig) entries))+                 L.writeFile sig (getEntryFileContent entry)+                 rawSystem "gpg" ["--verify",sig,sum]+                 removeFile sum+                 removeFile sig++  where sigName = makeSigName fp+        sum = translate ".sum" fp+        isSig = (==sigName) . Tar.entryPath+        sig = translate ".sig" fp++getEntryFileContent entry =+  case Tar.entryContent entry of+    NormalFile bytes _ -> bytes+    _ -> error "malformed signature in the tar archive"