Merge branch 'encryption'

This commit is contained in:
Joey Hess 2013-09-05 00:09:11 -04:00
commit fc7b5cfe7d
30 changed files with 448 additions and 242 deletions

View file

@ -52,7 +52,7 @@ setRemoteCredPair c storage = go =<< getRemoteCredPair c storage
return c
storeconfig creds key (Just cipher) = do
s <- liftIO $ encrypt (GpgOpts []) cipher
s <- liftIO $ encrypt [] cipher
(feedBytes $ L.pack $ encodeCredPair creds)
(readBytes $ return . L.unpack)
return $ M.insert key (toB64 s) c

112
Crypto.hs
View file

@ -8,6 +8,8 @@
- Licensed under the GNU GPL version 3 or higher.
-}
{-# LANGUAGE FlexibleInstances #-}
module Crypto (
Cipher,
KeyIds(..),
@ -22,9 +24,8 @@ module Crypto (
feedBytes,
readBytes,
encrypt,
decrypt,
GpgOpts(..),
getGpgOpts,
decrypt,
getGpgEncParams,
prop_HmacSha1WithCipher_sane
) where
@ -32,17 +33,18 @@ module Crypto (
import qualified Data.ByteString.Lazy as L
import Data.ByteString.Lazy.UTF8 (fromString)
import Control.Applicative
import qualified Data.Map as M
import Common.Annex
import qualified Utility.Gpg as Gpg
import Utility.Gpg.Types
import Types.Key
import Types.Crypto
import Types.Remote
{- The beginning of a Cipher is used for MAC'ing; the remainder is used
- as the GPG symmetric encryption passphrase. Note that the cipher
- itself is base-64 encoded, hence the string is longer than
- 'cipherSize': 683 characters, padded to 684.
- as the GPG symmetric encryption passphrase when using the hybrid
- scheme. Note that the cipher itself is base-64 encoded, hence the
- string is longer than 'cipherSize': 683 characters, padded to 684.
-
- The 256 first characters that feed the MAC represent at best 192
- bytes of entropy. However that's more than enough for both the
@ -67,53 +69,63 @@ cipherMac :: Cipher -> String
cipherMac (Cipher c) = take cipherBeginning c
{- Creates a new Cipher, encrypted to the specified key id. -}
genEncryptedCipher :: String -> Bool -> IO StorableCipher
genEncryptedCipher keyid highQuality = do
genEncryptedCipher :: String -> EncryptedCipherVariant -> Bool -> IO StorableCipher
genEncryptedCipher keyid variant highQuality = do
ks <- Gpg.findPubKeys keyid
random <- Gpg.genRandom highQuality cipherSize
encryptCipher (Cipher random) ks
random <- Gpg.genRandom highQuality size
encryptCipher (Cipher random) variant ks
where
size = case variant of
HybridCipher -> cipherSize -- used for MAC + symmetric
PubKeyCipher -> cipherBeginning -- only used for MAC
{- Creates a new, shared Cipher. -}
genSharedCipher :: Bool -> IO StorableCipher
genSharedCipher highQuality =
SharedCipher <$> Gpg.genRandom highQuality cipherSize
{- Updates an existing Cipher, re-encrypting it to add a keyid. -}
updateEncryptedCipher :: String -> StorableCipher -> IO StorableCipher
updateEncryptedCipher _ (SharedCipher _) = undefined
updateEncryptedCipher keyid encipher@(EncryptedCipher _ ks) = do
ks' <- Gpg.findPubKeys keyid
{- Updates an existing Cipher, re-encrypting it to add or remove keyids,
- depending on whether the first component is True or False. -}
updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher
updateEncryptedCipher _ SharedCipher{} = undefined
updateEncryptedCipher [] encipher = return encipher
updateEncryptedCipher newkeys encipher@(EncryptedCipher _ symmetric (KeyIds ks)) = do
dropKeys <- listKeyIds [ k | (False, k) <- newkeys ]
forM_ dropKeys $ \k -> unless (k `elem` ks) $
error $ "Key " ++ k ++ " was not present; cannot remove."
addKeys <- listKeyIds [ k | (True, k) <- newkeys ]
let ks' = (addKeys ++ ks) \\ dropKeys
when (null ks') $
error "Cannot remove the last key."
cipher <- decryptCipher encipher
encryptCipher cipher (merge ks ks')
encryptCipher cipher symmetric $ KeyIds ks'
where
merge (KeyIds a) (KeyIds b) = KeyIds $ a ++ b
listKeyIds = mapM (Gpg.findPubKeys >=*> keyIds) >=*> concat
describeCipher :: StorableCipher -> String
describeCipher (SharedCipher _) = "shared cipher"
describeCipher (EncryptedCipher _ (KeyIds ks)) =
"with gpg " ++ keys ks ++ " " ++ unwords ks
describeCipher (EncryptedCipher _ variant (KeyIds ks)) =
scheme ++ " with gpg " ++ keys ks ++ " " ++ unwords ks
where
scheme = case variant of
HybridCipher -> "hybrid cipher"
PubKeyCipher -> "pubkey crypto"
keys [_] = "key"
keys _ = "keys"
{- Encrypts a Cipher to the specified KeyIds. -}
encryptCipher :: Cipher -> KeyIds -> IO StorableCipher
encryptCipher (Cipher c) (KeyIds ks) = do
encryptCipher :: Cipher -> EncryptedCipherVariant -> KeyIds -> IO StorableCipher
encryptCipher (Cipher c) variant (KeyIds ks) = do
-- gpg complains about duplicate recipient keyids
let ks' = nub $ sort ks
encipher <- Gpg.pipeStrict (Params "--encrypt" : recipients ks') c
return $ EncryptedCipher encipher (KeyIds ks')
where
recipients l = force_recipients :
concatMap (\k -> [Param "--recipient", Param k]) l
-- Force gpg to only encrypt to the specified
-- recipients, not configured defaults.
force_recipients = Params "--no-encrypt-to --no-default-recipient"
let params = Gpg.pkEncTo ks' ++ Gpg.stdEncryptionParams False
encipher <- Gpg.pipeStrict params c
return $ EncryptedCipher encipher variant (KeyIds ks')
{- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -}
decryptCipher :: StorableCipher -> IO Cipher
decryptCipher (SharedCipher t) = return $ Cipher t
decryptCipher (EncryptedCipher t _) =
decryptCipher (EncryptedCipher t _ _) =
Cipher <$> Gpg.pipeStrict [ Param "--decrypt" ] t
{- Generates an encrypted form of a Key. The encryption does not need to be
@ -139,15 +151,21 @@ feedBytes = flip L.hPut
readBytes :: (L.ByteString -> IO a) -> Reader a
readBytes a h = L.hGetContents h >>= a
{- Runs a Feeder action, that generates content that is symmetrically encrypted
- with the Cipher using the given GnuPG options, and then read by the Reader
- action. -}
encrypt :: GpgOpts -> Cipher -> Feeder -> Reader a -> IO a
encrypt opts = Gpg.feedRead ( Params "--symmetric --force-mdc" : toParams opts )
. cipherPassphrase
{- Runs a Feeder action, that generates content that is symmetrically
- encrypted with the Cipher (unless it is empty, in which case
- public-key encryption is used) using the given gpg options, and then
- read by the Reader action. Note: For public-key encryption,
- recipients MUST be included in 'params' (for instance using
- 'getGpgEncParams'). -}
encrypt :: [CommandParam] -> Cipher -> Feeder -> Reader a -> IO a
encrypt params cipher = Gpg.feedRead params' pass
where
pass = cipherPassphrase cipher
params' = params ++ Gpg.stdEncryptionParams (not $ null pass)
{- Runs a Feeder action, that generates content that is decrypted with the
- Cipher, and read by the Reader action. -}
- Cipher (or using a private key if the Cipher is empty), and read by the
- Reader action. -}
decrypt :: Cipher -> Feeder -> Reader a -> IO a
decrypt = Gpg.feedRead [Param "--decrypt"] . cipherPassphrase
@ -161,3 +179,21 @@ prop_HmacSha1WithCipher_sane :: Bool
prop_HmacSha1WithCipher_sane = known_good == macWithCipher' HmacSha1 "foo" "bar"
where
known_good = "46b4ec586117154dacd49d664e5d63fdc88efb51"
{- Return some options suitable for GnuPG encryption, symmetric or not. -}
class LensGpgEncParams a where getGpgEncParams :: a -> [CommandParam]
{- Extract the GnuPG options from a pair of a Remote Config and a Remote
- Git Config. If the remote is configured to use public-key encryption,
- look up the recipient keys and add them to the option list. -}
instance LensGpgEncParams (RemoteConfig, RemoteGitConfig) where
getGpgEncParams (c,gc) = map Param (remoteAnnexGnupgOptions gc) ++ recipients
where
recipients = case M.lookup "encryption" c of
Just "pubkey" -> Gpg.pkEncTo $ maybe [] (split ",") $
M.lookup "cipherkeys" c
_ -> []
{- Extract the GnuPG options from a Remote. -}
instance LensGpgEncParams (RemoteA a) where
getGpgEncParams r = getGpgEncParams (config r, gitconfig r)

View file

@ -133,7 +133,7 @@ storeEncrypted r buprepo (cipher, enck) k _p =
sendAnnex k (rollback enck buprepo) $ \src -> do
params <- bupSplitParams r buprepo enck []
liftIO $ catchBoolIO $
encrypt (getGpgOpts r) cipher (feedFile src) $ \h ->
encrypt (getGpgEncParams r) cipher (feedFile src) $ \h ->
pipeBup params (Just h) Nothing
retrieve :: BupRepo -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool

View file

@ -41,7 +41,7 @@ gen r u c gc = do
cst <- remoteCost gc cheapRemoteCost
let chunksize = chunkSize c
return $ encryptableRemote c
(storeEncrypted dir (getGpgOpts gc) chunksize)
(storeEncrypted dir (getGpgEncParams (c,gc)) chunksize)
(retrieveEncrypted dir chunksize)
Remote {
uuid = u,
@ -129,7 +129,7 @@ store d chunksize k _f p = sendAnnex k (void $ remove d k) $ \src ->
storeSplit meterupdate chunksize dests
=<< L.readFile src
storeEncrypted :: FilePath -> GpgOpts -> ChunkSize -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted :: FilePath -> [CommandParam] -> ChunkSize -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted d gpgOpts chunksize (cipher, enck) k p = sendAnnex k (void $ remove d enck) $ \src ->
metered (Just p) k $ \meterupdate ->
storeHelper d chunksize enck k $ \dests ->

View file

@ -95,7 +95,7 @@ storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted r (cipher, enck) k p = sendAnnex k (void $ remove r enck) $ \src -> do
metered (Just p) k $ \meterupdate ->
storeHelper r enck $ \h ->
encrypt (getGpgOpts r) cipher (feedFile src)
encrypt (getGpgEncParams r) cipher (feedFile src)
(readBytes $ meteredWrite meterupdate h)
retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool

View file

@ -23,30 +23,52 @@ import Utility.Metered
- updated to be accessible to an additional encryption key. Or the user
- could opt to use a shared cipher, which is stored unencrypted. -}
encryptionSetup :: RemoteConfig -> Annex RemoteConfig
encryptionSetup c = case (M.lookup "encryption" c, extractCipher c) of
(Nothing, Nothing) -> error "Specify encryption=key or encryption=none or encryption=shared"
(Just "none", Nothing) -> return c
(Nothing, Just _) -> return c
(Just "shared", Just (SharedCipher _)) -> return c
(Just "none", Just _) -> cannotchange
(Just "shared", Just (EncryptedCipher _ _)) -> cannotchange
(Just _, Just (SharedCipher _)) -> cannotchange
(Just "shared", Nothing) -> use "encryption setup" . genSharedCipher
=<< highRandomQuality
(Just keyid, Nothing) -> use "encryption setup" . genEncryptedCipher keyid
=<< highRandomQuality
(Just keyid, Just v) -> use "encryption update" $ updateEncryptedCipher keyid v
encryptionSetup c = maybe genCipher updateCipher $ extractCipher c
where
cannotchange = error "Cannot change encryption type of existing remote."
-- The type of encryption
encryption = M.lookup "encryption" c
-- Generate a new cipher, depending on the chosen encryption scheme
genCipher = case encryption of
_ | M.member "cipher" c || M.member "cipherkeys" c -> cannotchange
Just "none" -> return c
Just "shared" -> use "encryption setup" . genSharedCipher
=<< highRandomQuality
-- hybrid encryption is the default when a keyid is
-- specified but no encryption
_ | maybe (M.member "keyid" c) (== "hybrid") encryption ->
use "encryption setup" . genEncryptedCipher key HybridCipher
=<< highRandomQuality
Just "pubkey" -> use "encryption setup" . genEncryptedCipher key PubKeyCipher
=<< highRandomQuality
_ -> error $ "Specify " ++ intercalate " or "
(map ("encryption=" ++)
["none","shared","hybrid","pubkey"])
++ "."
key = fromMaybe (error "Specifiy keyid=...") $ M.lookup "keyid" c
newkeys = maybe [] (\k -> [(True,k)]) (M.lookup "keyid+" c) ++
maybe [] (\k -> [(False,k)]) (M.lookup "keyid-" c)
cannotchange = error "Cannot set encryption type of existing remotes."
-- Update an existing cipher if possible.
updateCipher v = case v of
SharedCipher _ | maybe True (== "shared") encryption -> return c'
EncryptedCipher _ variant _
| maybe True (== if variant == HybridCipher then "hybrid" else "pubkey") encryption ->
use "encryption update" $ updateEncryptedCipher newkeys v
_ -> cannotchange
use m a = do
showNote m
cipher <- liftIO a
showNote $ describeCipher cipher
return $ M.delete "encryption" $ M.delete "highRandomQuality" $
storeCipher c cipher
return $ storeCipher c' cipher
highRandomQuality =
(&&) (maybe True ( /= "false") $ M.lookup "highRandomQuality" c)
<$> fmap not (Annex.getState Annex.fast)
c' = foldr M.delete c
-- git-annex used to remove 'encryption' as well, since
-- it was redundant; we now need to keep it for
-- public-key incryption, hence we leave it on newer
-- remotes (while being backward-compatible).
[ "keyid", "keyid+", "keyid-", "highRandomQuality" ]
{- Modifies a Remote to support encryption.
-
@ -111,27 +133,39 @@ embedCreds c
| isJust (M.lookup "cipherkeys" c) && isJust (M.lookup "cipher" c) = True
| otherwise = False
{- Gets encryption Cipher, and encrypted version of Key. -}
{- Gets encryption Cipher, and encrypted version of Key. In case we want
- asymmetric encryption, leave the first empty, but encrypt the Key
- regardless. (Empty ciphers imply asymmetric encryption.) We could
- also check how long is the cipher (MAC'ing-only ciphers are shorter),
- but we don't want to rely on that only. -}
cipherKey :: RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
cipherKey c k = maybe Nothing make <$> remoteCipher c
cipherKey c k = fmap make <$> remoteCipher c
where
make ciphertext = Just (ciphertext, encryptKey mac ciphertext k)
make ciphertext = (cipContent ciphertext, encryptKey mac ciphertext k)
cipContent
| M.lookup "encryption" c /= Just "pubkey" = id
| otherwise = const $ Cipher ""
mac = fromMaybe defaultMac $ M.lookup "mac" c >>= readMac
{- Stores an StorableCipher in a remote's configuration. -}
storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig
storeCipher c (SharedCipher t) = M.insert "cipher" (toB64 t) c
storeCipher c (EncryptedCipher t ks) =
storeCipher c (EncryptedCipher t _ ks) =
M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c
where
showkeys (KeyIds l) = intercalate "," l
{- Extracts an StorableCipher from a remote's configuration. -}
extractCipher :: RemoteConfig -> Maybe StorableCipher
extractCipher c =
case (M.lookup "cipher" c, M.lookup "cipherkeys" c) of
(Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks)
(Just t, Nothing) -> Just $ SharedCipher (fromB64 t)
_ -> Nothing
extractCipher c = case (M.lookup "cipher" c,
M.lookup "cipherkeys" c,
M.lookup "encryption" c) of
(Just t, Just ks, encryption) | maybe True (== "hybrid") encryption ->
Just $ EncryptedCipher (fromB64 t) HybridCipher (readkeys ks)
(Just t, Just ks, Just "pubkey") ->
Just $ EncryptedCipher (fromB64 t) PubKeyCipher (readkeys ks)
(Just t, Nothing, encryption) | maybe True (== "shared") encryption ->
Just $ SharedCipher (fromB64 t)
_ -> Nothing
where
readkeys = KeyIds . split ","

View file

@ -38,7 +38,7 @@ gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> Annex Remote
gen r u c gc = do
cst <- remoteCost gc expensiveRemoteCost
return $ encryptableRemote c
(storeEncrypted hooktype $ getGpgOpts gc)
(storeEncrypted hooktype $ getGpgEncParams (c,gc))
(retrieveEncrypted hooktype)
Remote {
uuid = u,
@ -118,7 +118,7 @@ store :: HookName -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store h k _f _p = sendAnnex k (void $ remove h k) $ \src ->
runHook h "store" k (Just src) $ return True
storeEncrypted :: HookName -> GpgOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted :: HookName -> [CommandParam] -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted h gpgOpts (cipher, enck) k _p = withTmp enck $ \tmp ->
sendAnnex k (void $ remove h enck) $ \src -> do
liftIO $ encrypt gpgOpts cipher (feedFile src) $

View file

@ -55,7 +55,7 @@ gen r u c gc = do
let o = RsyncOpts url (transport ++ opts) escape
islocal = rsyncUrlIsPath $ rsyncUrl o
return $ encryptableRemote c
(storeEncrypted o $ getGpgOpts gc)
(storeEncrypted o $ getGpgEncParams (c,gc))
(retrieveEncrypted o)
Remote
{ uuid = u
@ -137,7 +137,7 @@ rsyncUrls o k = map use annexHashes
store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
store o k _f p = sendAnnex k (void $ remove o k) $ rsyncSend o p k False
storeEncrypted :: RsyncOpts -> GpgOpts -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted :: RsyncOpts -> [CommandParam] -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted o gpgOpts (cipher, enck) k p = withTmp enck $ \tmp ->
sendAnnex k (void $ remove o enck) $ \src -> do
liftIO $ encrypt gpgOpts cipher (feedFile src) $

View file

@ -129,7 +129,7 @@ storeEncrypted r (cipher, enck) k p = s3Action r False $ \(conn, bucket) ->
-- To get file size of the encrypted content, have to use a temp file.
-- (An alternative would be chunking to to a constant size.)
withTmp enck $ \tmp -> sendAnnex k (void $ remove' r enck) $ \src -> do
liftIO $ encrypt (getGpgOpts r) cipher (feedFile src) $
liftIO $ encrypt (getGpgEncParams r) cipher (feedFile src) $
readBytes $ L.writeFile tmp
s3Bool =<< storeHelper (conn, bucket) r enck p tmp

View file

@ -94,7 +94,7 @@ storeEncrypted :: Remote -> (Cipher, Key) -> Key -> MeterUpdate -> Annex Bool
storeEncrypted r (cipher, enck) k p = metered (Just p) k $ \meterupdate ->
davAction r False $ \(baseurl, user, pass) ->
sendAnnex k (void $ remove r enck) $ \src ->
liftIO $ encrypt (getGpgOpts r) cipher
liftIO $ encrypt (getGpgEncParams r) cipher
(streamMeteredFile src meterupdate) $
readBytes $ storeHelper r enck baseurl user pass

52
Test.hs
View file

@ -29,6 +29,7 @@ import qualified Backend
import qualified Git.CurrentRepo
import qualified Git.Filename
import qualified Locations
import qualified Types.Crypto
import qualified Types.KeySource
import qualified Types.Backend
import qualified Types.TrustLevel
@ -41,6 +42,7 @@ import qualified Logs.Unused
import qualified Logs.Transfer
import qualified Logs.Presence
import qualified Remote
import qualified Remote.Helper.Encryptable
import qualified Types.Key
import qualified Types.Messages
import qualified Config
@ -874,18 +876,21 @@ test_bup_remote env = "git-annex bup remote" ~: intmpclonerepo env $ when Build.
-- gpg is not a build dependency, so only test when it's available
test_crypto :: TestEnv -> Test
test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ whenM (Utility.Path.inPath Utility.Gpg.gpgcmd) $ do
test_crypto env = "git-annex crypto" ~: TestList $ flip map ["shared","hybrid","pubkey"] $
\scheme -> TestCase $ intmpclonerepo env $ whenM (Utility.Path.inPath Utility.Gpg.gpgcmd) $ do
#ifndef mingw32_HOST_OS
Utility.Gpg.testTestHarness @? "test harness self-test failed"
Utility.Gpg.testHarness $ do
createDirectory "dir"
let a cmd = git_annex env cmd
let a cmd = git_annex env cmd $
[ "foo"
, "type=directory"
, "encryption=" ++ Utility.Gpg.testKeyId
, "encryption=" ++ scheme
, "directory=dir"
, "highRandomQuality=false"
]
] ++ if scheme `elem` ["hybrid","pubkey"]
then ["keyid=" ++ Utility.Gpg.testKeyId]
else []
a "initremote" @? "initremote failed"
not <$> a "initremote" @? "initremote failed to fail when run twice in a row"
a "enableremote" @? "enableremote failed"
@ -893,6 +898,16 @@ test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ whenM (Utility.Path
git_annex env "get" [annexedfile] @? "get of file failed"
annexed_present annexedfile
git_annex env "copy" [annexedfile, "--to", "foo"] @? "copy --to encrypted remote failed"
(c,k) <- annexeval $ do
uuid <- Remote.nameToUUID "foo"
rs <- Logs.Remote.readRemoteLog
Just (k,_) <- Backend.lookupFile annexedfile
return (fromJust $ M.lookup uuid rs, k)
let key = if scheme `elem` ["hybrid","pubkey"]
then Just $ Utility.Gpg.KeyIds [Utility.Gpg.testKeyId]
else Nothing
testEncryptedRemote scheme key c [k] @? "invalid crypto setup"
annexed_present annexedfile
git_annex env "drop" [annexedfile, "--numcopies=2"] @? "drop failed"
annexed_notpresent annexedfile
@ -900,8 +915,35 @@ test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ whenM (Utility.Path
annexed_present annexedfile
not <$> git_annex env "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"
annexed_present annexedfile
where
{- Ensure the configuration complies with the encryption scheme, and
- that all keys are encrypted properly for the given directory remote. -}
testEncryptedRemote scheme ks c keys = case Remote.Helper.Encryptable.extractCipher c of
Just cip@Crypto.SharedCipher{} | scheme == "shared" && isNothing ks ->
checkKeys cip Nothing
Just cip@(Crypto.EncryptedCipher encipher v ks')
| checkScheme v && keysMatch ks' ->
checkKeys cip (Just v) <&&> checkCipher encipher ks'
_ -> return False
where
keysMatch (Utility.Gpg.KeyIds ks') =
maybe False (\(Utility.Gpg.KeyIds ks2) ->
sort (nub ks2) == sort (nub ks')) ks
checkCipher encipher = Utility.Gpg.checkEncryptionStream encipher . Just
checkScheme Types.Crypto.HybridCipher = scheme == "hybrid"
checkScheme Types.Crypto.PubKeyCipher = scheme == "pubkey"
checkKeys cip mvariant = do
cipher <- Crypto.decryptCipher cip
files <- filterM doesFileExist $
map ("dir" </>) $ concatMap (key2files cipher) keys
return (not $ null files) <&&> allM (checkFile mvariant) files
checkFile mvariant filename =
Utility.Gpg.checkEncryptionFile filename $
if mvariant == Just Types.Crypto.PubKeyCipher then ks else Nothing
key2files cipher = Locations.keyPaths .
Crypto.encryptKey Types.Crypto.HmacSha1 cipher
#else
putStrLn "gpg testing not implemented on Windows"
putStrLn "gpg testing not implemented on Windows"
#endif
-- This is equivilant to running git-annex, but it's all run in-process

View file

@ -8,6 +8,7 @@
module Types.Crypto (
Cipher(..),
StorableCipher(..),
EncryptedCipherVariant(..),
KeyIds(..),
Mac(..),
readMac,
@ -24,7 +25,10 @@ import Utility.Gpg (KeyIds(..))
-- XXX ideally, this would be a locked memory region
newtype Cipher = Cipher String
data StorableCipher = EncryptedCipher String KeyIds | SharedCipher String
data StorableCipher = EncryptedCipher String EncryptedCipherVariant KeyIds
| SharedCipher String
deriving (Ord, Eq)
data EncryptedCipherVariant = HybridCipher | PubKeyCipher
deriving (Ord, Eq)
{- File names are (client-side) MAC'ed on special remotes.

View file

@ -24,7 +24,7 @@ import Utility.Env
import Utility.Tmp
#endif
newtype KeyIds = KeyIds [String]
newtype KeyIds = KeyIds { keyIds :: [String] }
deriving (Ord, Eq)
{- If a specific gpg command was found at configure time, use it.
@ -32,6 +32,10 @@ newtype KeyIds = KeyIds [String]
gpgcmd :: FilePath
gpgcmd = fromMaybe "gpg" SysConfig.gpg
-- Generate an argument list to asymetrically encrypt to the given recipients.
pkEncTo :: [String] -> [CommandParam]
pkEncTo = concatMap (\r -> [Param "--recipient", Param r])
stdParams :: [CommandParam] -> IO [String]
stdParams params = do
#ifndef mingw32_HOST_OS
@ -48,9 +52,21 @@ stdParams params = do
return $ defaults ++ toCommand params
#endif
where
-- be quiet, even about checking the trustdb
-- Be quiet, even about checking the trustdb. If the one of the
-- default param is already present in 'params', don't include it
-- twice in the output list.
defaults = ["--quiet", "--trust-model", "always"]
{- Usual options for symmetric / public-key encryption. -}
stdEncryptionParams :: Bool -> [CommandParam]
stdEncryptionParams symmetric = [enc symmetric, Param "--force-mdc"]
where
enc True = Param "--symmetric"
-- Force gpg to only encrypt to the specified recipients, not
-- configured defaults. Recipients are assumed to be specified in
-- elsewhere.
enc False = Params "--encrypt --no-encrypt-to --no-default-recipient"
{- Runs gpg with some params and returns its stdout, strictly. -}
readStrict :: [CommandParam] -> IO String
readStrict params = do
@ -71,10 +87,11 @@ pipeStrict params input = do
hClose to
hGetContentsStrict from
{- Runs gpg with some parameters. First sends it a passphrase via
- --passphrase-fd. Then runs a feeder action that is passed a handle and
- should write to it all the data to input to gpg. Finally, runs
- a reader action that is passed a handle to gpg's output.
{- Runs gpg with some parameters. First sends it a passphrase (unless it
- is empty) via '--passphrase-fd'. Then runs a feeder action that is
- passed a handle and should write to it all the data to input to gpg.
- Finally, runs a reader action that is passed a handle to gpg's
- output.
-
- Runs gpg in batch mode; this is necessary to avoid gpg 2.x prompting for
- the passphrase.
@ -82,27 +99,28 @@ pipeStrict params input = do
- Note that to avoid deadlock with the cleanup stage,
- the reader must fully consume gpg's input before returning. -}
feedRead :: [CommandParam] -> String -> (Handle -> IO ()) -> (Handle -> IO a) -> IO a
feedRead params passphrase feeder reader = do
feedRead params passphrase feeder reader = if null passphrase
then go =<< stdParams (Param "--batch" : params)
else do
#ifndef mingw32_HOST_OS
-- pipe the passphrase into gpg on a fd
(frompipe, topipe) <- createPipe
void $ forkIO $ do
toh <- fdToHandle topipe
hPutStrLn toh passphrase
hClose toh
let Fd pfd = frompipe
let passphrasefd = [Param "--passphrase-fd", Param $ show pfd]
-- pipe the passphrase into gpg on a fd
(frompipe, topipe) <- createPipe
void $ forkIO $ do
toh <- fdToHandle topipe
hPutStrLn toh passphrase
hClose toh
let Fd pfd = frompipe
let passphrasefd = [Param "--passphrase-fd", Param $ show pfd]
params' <- stdParams $ [Param "--batch"] ++ passphrasefd ++ params
closeFd frompipe `after` go params'
params' <- stdParams $ Param "--batch" : passphrasefd ++ params
closeFd frompipe `after` go params'
#else
-- store the passphrase in a temp file for gpg
withTmpFile "gpg" $ \tmpfile h -> do
hPutStr h passphrase
hClose h
-- store the passphrase in a temp file for gpg
withTmpFile "gpg" $ \tmpfile h -> do
hPutStr h passphrase
hClose h
let passphrasefile = [Param "--passphrase-file", File tmpfile]
params' <- stdParams $ [Param "--batch"] ++ passphrasefile ++ params
go params'
go =<< stdParams $ Param "--batch" : passphrasefile ++ params
#endif
where
go params' = withBothHandles createProcessSuccess (proc gpgcmd params')
@ -260,3 +278,41 @@ testTestHarness = do
keys <- testHarness $ findPubKeys testKeyId
return $ KeyIds [testKeyId] == keys
#endif
#ifndef mingw32_HOST_OS
checkEncryptionFile :: FilePath -> Maybe KeyIds -> IO Bool
checkEncryptionFile filename keys =
checkGpgPackets keys =<< readStrict params
where
params = [Params "--list-packets --list-only", File filename]
checkEncryptionStream :: String -> Maybe KeyIds -> IO Bool
checkEncryptionStream stream keys =
checkGpgPackets keys =<< pipeStrict params stream
where
params = [Params "--list-packets --list-only"]
{- Parses an OpenPGP packet list, and checks whether data is
- symmetrically encrypted (keys is Nothing), or encrypted to some
- public key(s).
- /!\ The key needs to be in the keyring! -}
checkGpgPackets :: Maybe KeyIds -> String -> IO Bool
checkGpgPackets keys str = do
let (asym,sym) = partition (pubkeyEncPacket `isPrefixOf`) $
filter (\l' -> pubkeyEncPacket `isPrefixOf` l' ||
symkeyEncPacket `isPrefixOf` l') $
takeWhile (/= ":encrypted data packet:") $
lines str
case (keys,asym,sym) of
(Nothing, [], [_]) -> return True
(Just (KeyIds ks), ls, []) -> do
-- Find the master key associated with the
-- encryption subkey.
ks' <- concat <$> mapM (findPubKeys >=*> keyIds)
[ k | k:"keyid":_ <- map (reverse . words) ls ]
return $ sort (nub ks) == sort (nub ks')
_ -> return False
where
pubkeyEncPacket = ":pubkey enc packet: "
symkeyEncPacket = ":symkey enc packet: "
#endif

View file

@ -1,30 +0,0 @@
{- gpg data types
-
- Copyright 2013 guilhem <guilhem@fripost.org>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Utility.Gpg.Types where
import Utility.SafeCommand
import Types.GitConfig
import Types.Remote
{- GnuPG options. -}
type GpgOpt = String
newtype GpgOpts = GpgOpts [GpgOpt]
toParams :: GpgOpts -> [CommandParam]
toParams (GpgOpts opts) = map Param opts
class LensGpgOpts a where
getGpgOpts :: a -> GpgOpts
{- Extract the GnuPG options from a Remote Git Config. -}
instance LensGpgOpts RemoteGitConfig where
getGpgOpts = GpgOpts . remoteAnnexGnupgOptions
{- Extract the GnuPG options from a Remote. -}
instance LensGpgOpts (RemoteA a) where
getGpgOpts = getGpgOpts . gitconfig

13
debian/changelog vendored
View file

@ -11,6 +11,19 @@ git-annex (4.20130828) UNRELEASED; urgency=low
from feeds.
* Honor core.sharedrepository when receiving and adding files in direct
mode.
* initremote: Syntax change when setting up encrypted special remote.
Now use: encryption=hybrid keyid=$KEYID
(Thanks, guilhem for the patch.)
* enableremote: gpg keys can be removed from those a remote encrypts
to by passing "keyid-=$KEYID". keyid+= is also provided, although
"encryption=$KEYID" can also be used as always.
(Thanks, guilhem for the patch.)
* Added encryption=pubkey scheme, which encrypts to public keys directly
rather than the hybrid approach. See documentation for advantages
and disadvantages, but encryption=hybrid is the recommended scheme still.
(Thanks, guilhem for the patch.)
-- Joey Hess <joeyh@debian.org> Tue, 27 Aug 2013 11:03:00 -0400
git-annex (4.20130827) unstable; urgency=low

4
debian/copyright vendored
View file

@ -14,10 +14,6 @@ Copyright: 2011 Bas van Dijk & Roel van Dijk
2012 Joey Hess <joey@kitenet.net>
License: GPL-3+
Files: Utility/Gpg/Types.hs
Copyright: 2013 guilhem <guilhem@fripost.org>
License: GPL-3+
Files: doc/logo* */favicon.ico standalone/osx/git-annex.app/Contents/Resources/git-annex.icns standalone/android/icons/*
Copyright: 2007 Henrik Nyh <http://henrik.nyh.se/>
2010 Joey Hess <joey@kitenet.net>

View file

@ -29,3 +29,6 @@ git-annex: user error (gpg ["--quiet","--trust-model","always","--encrypt","--no
failed
git-annex: enableremote: 1 failed
"""]]
> [[done]]; can now use: `git annex enableremote foo keyid-=REVOKEDKEY
> keyid+=NEWKEY` to remove it, and add a new key. --[[Joey]]

View file

@ -103,14 +103,17 @@ use the special remote.
## risks
A risk of this scheme is that, once the symmetric cipher has been obtained, it
allows full access to all the encrypted content. This scheme does not allow
revoking a given gpg key access to the cipher, since anyone with such a key
could have already decrypted the cipher and stored a copy.
A risk of this scheme is that, once the symmetric cipher has been
obtained, it allows full access to all the encrypted content. Indeed
anyone owning a key that used to be granted access could already have
decrypted the cipher and stored a copy. While it is in possible to
remove a key with `keyid-=`, it is designed for a
[[completely_different_purpose|/encryption]] and does not actually revoke
access.
If git-annex stores the decrypted symmetric cipher in memory, then there
is a risk that it could be intercepted from there by an attacker. Gpg
amelorates these type of risks by using locked memory. For git-annex, note
ameliorates these type of risks by using locked memory. For git-annex, note
that an attacker with local machine access can tell at least all the
filenames and metadata of files stored in the encrypted remote anyway,
and can access whatever content is stored locally.

View file

@ -6,20 +6,90 @@ Encryption is needed when using [[special_remotes]] like Amazon S3, where
file content is sent to an untrusted party who does not have access to the
git repository.
Such an encrypted remote uses strong GPG encryption on the contents of files,
as well as HMAC hashing of the filenames. The size of the encrypted files,
and access patterns of the data, should be the only clues to what is
stored in such a remote.
Such an encrypted remote uses strong ([[symmetric|design/encryption]] or
asymmetric) encryption on the contents of files, as well as HMAC hashing
of the filenames. The size of the encrypted files, and access patterns
of the data, should be the only clues to what is stored in such a
remote.
You should decide whether to use encryption with a special remote before
any data is stored in it. So, `git annex initremote` requires you
to specify "encryption=none" when first setting up a remote in order
to disable encryption.
to disable encryption. To use encryption, you run
run `git-annex initremote` in one of these ways:
If you want to use encryption, run `git annex initremote` with
"encryption=USERID". The value will be passed to `gpg` to find encryption keys.
Typically, you will say "encryption=2512E3C7" to use a specific gpg key.
Or, you might say "encryption=joey@kitenet.net" to search for matching keys.
* `git annex initremote newremote type=... encryption=hybrid keyid=KEYID ...`
* `git annex initremote newremote type=... encryption=shared`
* `git annex initremote newremote type=... encryption=pubkey keyid=KEYID ...`
## hybrid encryption keys
The [[hybrid_key_design|design/encryption]] allows additional
encryption keys to be added on to a special remote later. Due to this
flexability, it is the default and recommended encryption scheme.
git annex initremote newremote type=... [encryption=hybrid] keyid=KEYID ...
Here the KEYID(s) are passed to `gpg` to find encryption keys.
Typically, you will say "keyid=2512E3C7" to use a specific gpg key.
Or, you might say "keyid=joey@kitenet.net" to search for matching keys.
To add a new key and allow it to access all the content that is stored
in the encrypted special remote, just run `git annex
enableremote` specifying the new encryption key:
git annex enableremote myremote keyid+=788A3F4C
While a key can later be removed from the list, note that
that will **not** necessarily prevent the owner of the key
from accessing data on the remote (which is by design impossible to prevent,
short of deleting the remote). In fact the only sound use of `keyid-=` is
probably to replace a revoked key:
git annex enableremote myremote keyid-=2512E3C7 keyid+=788A3F4C
See also [[encryption_design|design/encryption]] for other security
risks associated with encryption.
## shared encryption key
Alternatively, you can configure git-annex to use a shared cipher to
encrypt data stored in a remote. This shared cipher is stored,
**unencrypted** in the git repository. So it's shared among every
clone of the git repository.
git annex initremote newremote type=... encryption=shared
The advantage is you don't need to set up gpg keys. The disadvantage is
that this is **insecure** unless you trust every clone of the git
repository with access to the encrypted data stored in the special remote.
## regular public key encryption
This alternative simply encrypts the files in the special remotes to one or
more public keys. It might be considered more secure due to its simplicity
and since it's exactly the way everyone else uses gpg.
git annex initremote newremote type=.... encryption=pubkey keyid=KEYID ...
A disavantage is that is not easy to later add additional public keys
to the special remote. While the `enableremote` parameters `keyid+=` and
`keyid-=` can be used, they have **no effect** on files that are already
present on the remote. Probably the only use for these parameters is
to replace a revoked key:
git annex enableremote myremote keyid-=2512E3C7 keyid+=788A3F4C
But even in this case, since the files are not re-encrypted, the revoked
key has to be kept around to be able to decrypt those files.
(Of course, if the reason for revocation is
that the key has been compromised, it is **insecure** to leave files
encrypted using that old key, and the user should re-encrypt everything.)
(Because filenames are MAC'ed, a cipher still needs to be
generated (and encrypted to the given key IDs).)
## MAC algorithm
The default MAC algorithm to be applied on the filenames is HMACSHA1. A
stronger one, for instance HMACSHA512, one can be chosen upon creation
@ -27,29 +97,3 @@ of the special remote with the option `mac=HMACSHA512`. The available
MAC algorithms are HMACSHA1, HMACSHA224, HMACSHA256, HMACSHA384, and
HMACSHA512. Note that it is not possible to change algorithm for a
non-empty remote.
The [[encryption_design|design/encryption]] allows additional encryption keys
to be added on to a special remote later. Once a key is added, it is able
to access content that has already been stored in the special remote.
To add a new key, just run `git annex enableremote` specifying the
new encryption key:
git annex enableremote myremote encryption=788A3F4C
Note that once a key has been given access to a remote, it's not
possible to revoke that access, short of deleting the remote. See
[[encryption_design|design/encryption]] for other security risks
associated with encryption.
## shared cipher mode
Alternatively, you can configure git-annex to use a shared cipher to
encrypt data stored in a remote. This shared cipher is stored,
**unencrypted** in the git repository. So it's shared amoung every
clone of the git repository. The advantage is you don't need to set up gpg
keys. The disadvantage is that this is **insecure** unless you
trust every clone of the git repository with access to the encrypted data
stored in the special remote.
To use shared encryption, specify "encryption=shared" when first setting
up a special remote.

View file

@ -307,19 +307,30 @@ subdirectories).
types of special remotes need different configuration values. The
command will prompt for parameters as needed.
All special remotes support encryption. You must either specify
encryption=none to disable encryption, or use encryption=keyid
(or encryption=emailaddress) to specify a gpg key that can access
the encrypted special remote.
All special remotes support encryption. You can either specify
`encryption=none` to disable encryption, or specify
`encryption=hybrid keyid=$keyid ...` to specify a gpg key id (or an email
address accociated with a key.
Note that with encryption enabled, a gpg key is created. This requires
sufficient entropy. If initremote seems to hang or take a long time
while generating the key, you may want to ctrl-c it and re-run with --fast,
which causes it to use a lower-quality source of randomness.
There are actually three schemes that can be used for management of the
encryption keys. When using the encryption=hybrid scheme, additional
gpg keys can be given access to the encrypted special remote easily
(without re-encrypting everything). When using encryption=shared,
a shared key is generated and stored in the git repository, allowing
anyone who can clone the git repository to access it. Finally, when using
encryption=pubkey, content in the special remote is directly encrypted
to the specified gpg keys, and additional ones cannot easily be given
access.
Note that with encryption enabled, a cryptographic key is created.
This requires sufficient entropy. If initremote seems to hang or take
a long time while generating the key, you may want to ctrl-c it and
re-run with --fast, which causes it to use a lower-quality source of
randomness.
Example Amazon S3 remote:
git annex initremote mys3 type=S3 encryption=me@example.com datacenter=EU
git annex initremote mys3 type=S3 encryption=hybrid keyid=me@example.com datacenter=EU
* enableremote name [param=value ...]
@ -335,11 +346,28 @@ subdirectories).
For example, the directory special remote requires a directory= parameter.
This command can also be used to modify the configuration of an existing
special remote, by specifying new values for parameters that were originally
set when using initremote. For example, to add a new gpg key to the keys
that can access an encrypted remote:
special remote, by specifying new values for parameters that were
originally set when using initremote. (However, some settings such as
the as the encryption scheme cannot be changed once a special remote
has been created.)
git annex enableremote mys3 encryption=friend@example.com
The gpg keys that an encrypted special remote is encrypted to can be
changed using the keyid+= and keyid-= parameters. These respectively
add and remove keys from the list. However, note that removing a key
does NOT necessarily prevent the key's owner from accessing data
in the encrypted special remote
(which is by design impossible, short of deleting the remote).
One use-case of keyid-= is to replace a revoked key with
a new key:
git annex enableremote mys3 keyid-=revokedkey keyid+=newkey
Also, note that for encrypted special remotes using plain public-key
encryption (encryption=pubkey), adding or removing a key has NO effect
on files that have already been copied to the remote. Hence using
keyid+= and keyid-= with such remotes should be used with care, and
make little sense except in cases like the revoked key example above.
* trust [repository ...]

View file

@ -15,13 +15,10 @@ can read inside the local git repository.
A number of parameters can be passed to `git annex initremote` to configure
the S3 remote.
* `encryption` - Required. Either "none" to disable encryption (not recommended),
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to access the encrypted data (use with caution).
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
* `embedcreds` - Optional. Set to "yes" embed the login credentials inside
the git repository, which allows other clones to also access them. This is

View file

@ -19,14 +19,10 @@ for example; or clone bup's git repository to further back it up.
These parameters can be passed to `git annex initremote` to configure bup:
* `encryption` - Required. Either "none" to disable encryption of content
stored in bup (ssh will still be used to transport it securely),
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to access the encrypted data (use with caution).
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
* `buprepo` - Required. This is passed to `bup` as the `--remote`
to use to store data. To create the repository,`bup init` will be run.

View file

@ -14,13 +14,10 @@ Instead, you should use a regular `git clone` of your git-annex repository.
These parameters can be passed to `git annex initremote` to configure the
remote:
* `encryption` - Required. Either "none" to disable encryption,
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to decrypt the encrypted data.
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
* `chunksize` - Avoid storing files larger than the specified size in the
directory. For use on directories on mount points that have file size

View file

@ -21,13 +21,10 @@ can read inside the local git repository.
A number of parameters can be passed to `git annex initremote` to configure
the Glacier remote.
* `encryption` - Required. Either "none" to disable encryption (not recommended),
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to access the encrypted data (use with caution).
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
* `embedcreds` - Optional. Set to "yes" embed the login credentials inside
the git repository, which allows other clones to also access them. This is

View file

@ -25,17 +25,14 @@ Can you spot the potential data loss bugs in the above simple example?
These parameters can be passed to `git annex initremote`:
* `encryption` - Required. Either "none" to disable encryption,
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to access the encrypted data.
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `hooktype` - Required. This specifies a collection of hooks to use for
this remote.
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
## hooks
Each type of hook remote is specified by a collection of hook commands.

View file

@ -2,26 +2,22 @@ This special remote type rsyncs file contents to somewhere else.
Setup example:
# git annex initremote myrsync type=rsync rsyncurl=rsync://rsync.example.com/myrsync encryption=joey@kitenet.net
# git annex initremote myrsync type=rsync rsyncurl=rsync://rsync.example.com/myrsync keyid=joey@kitenet.net
# git annex describe myrsync "rsync server"
Or for using rsync over SSH
# git annex initremote myrsync type=rsync rsyncurl=ssh.example.com:/myrsync encryption=joey@kitenet.net
# git annex initremote myrsync type=rsync rsyncurl=ssh.example.com:/myrsync keyid=joey@kitenet.net
# git annex describe myrsync "rsync server"
## configuration
These parameters can be passed to `git annex initremote` to configure rsync:
* `encryption` - Required. Either "none" to disable encryption of content
stored on the remote rsync server,
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to decrypt the encrypted data.
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
* `rsyncurl` - Required. This is the url or `hostname:/directory` to
pass to rsync to tell it where to store content.

View file

@ -10,13 +10,10 @@ can read inside the local git repository.
A number of parameters can be passed to `git annex initremote` to configure
the webdav remote.
* `encryption` - Required. Either "none" to disable encryption (not recommended),
or a value that can be looked up (using gpg -k) to find a gpg encryption
key that will be given access to the remote, or "shared" which allows
every clone of the repository to access the encrypted data (use with caution).
* `encryption` - One of "none", "hybrid", "shared", or "pubkey".
See [[encryption]].
Note that additional gpg keys can be given access to a remote by
running enableremote with the new key id. See [[encryption]].
* `keyid` - Specifies the gpg key to use for [[encryption]].
* `embedcreds` - Optional. Set to "yes" embed the login credentials inside
the git repository, which allows other clones to also access them. This is
@ -42,4 +39,4 @@ the webdav remote.
Setup example:
# WEBDAV_USERNAME=joey@kitenet.net WEBDAV_PASSWORD=xxxxxxx git annex initremote box.com type=webdav url=https://www.box.com/dav/git-annex chunksize=75mb encryption=joey@kitenet.net
# WEBDAV_USERNAME=joey@kitenet.net WEBDAV_PASSWORD=xxxxxxx git annex initremote box.com type=webdav url=https://www.box.com/dav/git-annex chunksize=75mb keyid=joey@kitenet.net

View file

@ -16,7 +16,7 @@ like "2512E3C7"
Next, create the Glacier remote.
# git annex initremote glacier type=glacier encryption=2512E3C7
# git annex initremote glacier type=glacier keyid=2512E3C7
initremote glacier (encryption setup with gpg key C910D9222512E3C7) (gpg) ok
The configuration for the Glacier remote is stored in git. So to make another

View file

@ -14,7 +14,7 @@ like "2512E3C7"
Next, create the S3 remote, and describe it.
# git annex initremote cloud type=S3 encryption=2512E3C7
# git annex initremote cloud type=S3 keyid=2512E3C7
initremote cloud (encryption setup with gpg key C910D9222512E3C7) (checking bucket) (creating bucket in US) (gpg) ok
# git annex describe cloud "at Amazon's US datacenter"
describe cloud ok

View file

@ -5,7 +5,7 @@ for providing 50 gb of free storage if you sign up with its Android client.
git-annex can use Box as a [[special remote|special_remotes]].
Recent versions of git-annex make this very easy to set up:
WEBDAV_USERNAME=you@example.com WEBDAV_PASSWORD=xxxxxxx git annex initremote box.com type=webdav url=https://www.box.com/dav/git-annex chunksize=75mb encryption=you@example.com
WEBDAV_USERNAME=you@example.com WEBDAV_PASSWORD=xxxxxxx git annex initremote box.com type=webdav url=https://www.box.com/dav/git-annex chunksize=75mb encryption=shared
Note the use of chunksize; Box has a 100 mb maximum file size, and this
breaks up large files into chunks before that limit is reached.