diff options
-rw-r--r-- | Annex/UUID.hs | 22 | ||||
-rw-r--r-- | Git/GCrypt.hs | 73 | ||||
-rw-r--r-- | Remote/Git.hs | 7 | ||||
-rw-r--r-- | doc/bugs/Error_creating_encrypted_cloud_repository:___34__internal_server_error__34__.mdwn | 2 | ||||
-rw-r--r-- | doc/design/gcrypt.mdwn | 8 | ||||
-rw-r--r-- | doc/devblog/day_3__gcrypt_uuids.mdwn | 63 |
6 files changed, 175 insertions, 0 deletions
diff --git a/Annex/UUID.hs b/Annex/UUID.hs index c36861bbe..4e274503b 100644 --- a/Annex/UUID.hs +++ b/Annex/UUID.hs @@ -17,8 +17,11 @@ module Annex.UUID ( getUncachedUUID, prepUUID, genUUID, + genUUIDInNameSpace, + gCryptNameSpace, removeRepoUUID, storeUUID, + setUUID, ) where import Common.Annex @@ -27,7 +30,9 @@ import qualified Git.Config import Config import qualified Data.UUID as U +import qualified Data.UUID.V5 as U5 import System.Random +import Data.Bits.Utils configkey :: ConfigKey configkey = annexConfig "uuid" @@ -36,6 +41,17 @@ configkey = annexConfig "uuid" genUUID :: IO UUID genUUID = UUID . show <$> (randomIO :: IO U.UUID) +{- Generates a UUID from a given string, using a namespace. + - Given the same namespace, the same string will always result + - in the same UUID. -} +genUUIDInNameSpace :: U.UUID -> String -> UUID +genUUIDInNameSpace namespace = UUID . show . U5.generateNamed namespace . s2w8 + +{- Namespace used for UUIDs derived from git-remote-gcrypt ids. -} +gCryptNameSpace :: U.UUID +gCryptNameSpace = U5.generateNamed U5.namespaceURL $ + s2w8 "http://git-annex.branchable.com/design/gcrypt/" + {- Get current repository's UUID. -} getUUID :: Annex UUID getUUID = getRepoUUID =<< gitRepo @@ -72,3 +88,9 @@ prepUUID = whenM ((==) NoUUID <$> getUUID) $ storeUUID :: ConfigKey -> UUID -> Annex () storeUUID configfield = setConfig configfield . fromUUID + +{- Only sets the configkey in the Repo; does not change .git/config -} +setUUID :: Git.Repo -> UUID -> IO Git.Repo +setUUID r u = do + let s = show configkey ++ "=" ++ fromUUID u + Git.Config.store s r diff --git a/Git/GCrypt.hs b/Git/GCrypt.hs new file mode 100644 index 000000000..e22bd74a2 --- /dev/null +++ b/Git/GCrypt.hs @@ -0,0 +1,73 @@ +{- git-remote-gcrypt support + - + - https://github.com/blake2-ppc/git-remote-gcrypt + - + - Copyright 2013 Joey Hess <joey@kitenet.net> + - + - Licensed under the GNU GPL version 3 or higher. + -} + +module Git.GCrypt where + +import Common +import Git.Types +import Git.Construct +import qualified Git.Config as Config +import Utility.Gpg + +urlPrefix :: String +urlPrefix = "gcrypt::" + +isEncrypted :: Repo -> Bool +isEncrypted Repo { location = Url url } = urlPrefix `isPrefixOf` show url +isEncrypted _ = False + +{- The first Repo is the git repository that has the second Repo + - as one of its remotes. + - + - When the remote Repo uses gcrypt, returns the actual underlying + - git repository that gcrypt is using to store its data. + - + - Throws an exception if an url is invalid or the repo does not use + - gcrypt. + -} +encryptedRepo :: Repo -> Repo -> IO Repo +encryptedRepo baserepo = go + where + go Repo { location = Url url } + | urlPrefix `isPrefixOf` u = + fromRemoteLocation (drop plen u) baserepo + | otherwise = notencrypted + where + u = show url + plen = length urlPrefix + go _ = notencrypted + notencrypted = error "not a gcrypt encrypted repository" + +{- gcrypt gives each encrypted repository a uique gcrypt-id, + - which is stored in the repository (in encrypted form) + - and cached in a per-remote gcrypt-id configuration setting. -} +remoteRepoId :: Repo -> Repo -> Maybe String +remoteRepoId = getRemoteConfig "gcrypt-id" + +getRemoteConfig :: String -> Repo -> Repo -> Maybe String +getRemoteConfig field baserepo remote = do + name <- remoteName remote + Config.getMaybe (remoteConfigKey field name) baserepo + +{- Gpg keys that the remote is encrypted for. + - If empty, gcrypt uses --default-recipient-self -} +particiantList :: Maybe Repo -> Repo -> Repo -> KeyIds +particiantList globalconfigrepo baserepo remote = KeyIds $ parse $ firstJust + [ getRemoteConfig "participants" baserepo remote + , Config.getMaybe defaultkey baserepo + , Config.getMaybe defaultkey =<< globalconfigrepo + ] + where + defaultkey = "gcrypt.participants" + parse (Just "simple") = [] + parse (Just l) = words l + parse Nothing = [] + +remoteConfigKey :: String -> String -> String +remoteConfigKey key field = "remote." ++ field ++ "." ++ key diff --git a/Remote/Git.hs b/Remote/Git.hs index e269b9ad8..b3f64bfb8 100644 --- a/Remote/Git.hs +++ b/Remote/Git.hs @@ -26,6 +26,7 @@ import qualified Git import qualified Git.Config import qualified Git.Construct import qualified Git.Command +import qualified Git.GCrypt import qualified Annex import Logs.Presence import Logs.Transfer @@ -152,6 +153,12 @@ tryGitConfigRead r | Git.repoIsHttp r = do headers <- getHttpHeaders store $ geturlconfig headers + | Git.GCrypt.isEncrypted r = do + g <- gitRepo + case Git.GCrypt.remoteRepoId g r of + Nothing -> return r + Just v -> store $ liftIO $ setUUID r $ + genUUIDInNameSpace gCryptNameSpace v | Git.repoIsUrl r = return r | otherwise = store $ safely $ onLocal r $ do ensureInitialized diff --git a/doc/bugs/Error_creating_encrypted_cloud_repository:___34__internal_server_error__34__.mdwn b/doc/bugs/Error_creating_encrypted_cloud_repository:___34__internal_server_error__34__.mdwn index 251c6aa08..4d04718ab 100644 --- a/doc/bugs/Error_creating_encrypted_cloud_repository:___34__internal_server_error__34__.mdwn +++ b/doc/bugs/Error_creating_encrypted_cloud_repository:___34__internal_server_error__34__.mdwn @@ -20,3 +20,5 @@ The operating system is Mac OS X 10.8.4, and the version of git-annex is 4.20130 # End of transcript or log. """]] + +[[!meta title "OSX bundled gpg does not work with gpg.conf created by MacGPG"]] diff --git a/doc/design/gcrypt.mdwn b/doc/design/gcrypt.mdwn new file mode 100644 index 000000000..d5b9c064b --- /dev/null +++ b/doc/design/gcrypt.mdwn @@ -0,0 +1,8 @@ +To integrate with git-remote-gcrypt, a key thing is to have a way to map +from the gcrypt-id of an encrypted repository to a git-annex repository +uuid. + +To do this, we'll make a v5 UUID, feeding in the gcrypt-id. +The namespace used is itself a v5 UUID, generated using the URL +namespace and the URL of this page at the time this scheme was +developed: "http://git-annex.branchable.com/design/gcrypt/" diff --git a/doc/devblog/day_3__gcrypt_uuids.mdwn b/doc/devblog/day_3__gcrypt_uuids.mdwn new file mode 100644 index 000000000..3182aca63 --- /dev/null +++ b/doc/devblog/day_3__gcrypt_uuids.mdwn @@ -0,0 +1,63 @@ +Started work on [gcrypt](https://github.com/blake2-ppc/git-remote-gcrypt) +support. + +The first question is, should git-annex leave it up to gcrypt to transport +the data to the encrypted repository on a push/pull? gcrypt hooks into git +nicely to make that just work. However, if I go this route, it limits +the places the encrypted git repositores can be stored to regular git +remotes (and rsync). The alternative is to somehow use gcrypt to +generate/consume the data, but use the git-annex special remotes to store +individual files. Which would allow for a git repo stored on S3, etc. +For now, I am going with the simple option, but I have not ruled out +trying to make the latter work. It seems it would need changes to gcrypt +though. + +Next question: Given a remote that uses gcrypt, how do I determine the +annex.uuid of that repository. I found a nice solutuon to this. gcrypt has +its own gcrypt-id, and I convert it to a UUID in a +[[reproducible, and even standards-compliant way|design/gcrypt]]. So +the same encrypted remote will automatically get the same annex.uuid +wherever it's used. Nice. Does mean that git-annex cannot find a uuid +until `git pull` or `git push` has been used, to let gcrypt get the +gcrypt-id. Implemented that. + +The next step is actually making git-annex store data on gcrypt remotes. +And it needs to store it encrypted of course. It seems best to avoid +needing a `git annex initremote` for these gcrypt remotes, and just have +git-annex automatically encrypt data stored on them. But I don't +know. Without initializing them like a special remote is, I'm limited to +using the gpg keys that gcrypt is configured to encrypt to, and cannot use +the regular git-annex hybrid encryption scheme. Also, I need to generate +and store a nonce anyway to HMAC ecrypt keys. (Or modify gcrypt +to put enough entropy in gcrypt-id that I can use it?) + +Another concern I have is that gcrypt's own encryption scheme is simply +to use a list of public keys to encrypt to. It would be nicer if the +full set of git-annex encryption schemes could be used. Then the webapp +could use shared encryption to avoid needing to make the user set up a gpg +key, or hybrid encryption could be used to add keys later, etc. + +But I see why gcrypt works the way it does. Otherwise, you can't make an +encrypted repo with a friend set as one of the particpants and have them be +able to git clone it. Both hybrid and shared encryption store a secret +inside the repo, which is not accessible if it's encrypted using that +secret. There are use cases where not being able to blindly clone a gcrypt +repo would be ok. For example, you use the assistant to pair with a friend +and then set up an encrypted repo in the cloud for both of you to use. + +Anyway, for now, I will need to deal with +setting up gpg keys etc in the assistant. I don't want to tackle +full [[design/assistant/gpgkeys]] yet. Instead, I think I will start by +adding some simple stuff to the assistant: + +* When adding a USB drive, offer to encrypt the repository on the drive + so that only you can see it. +* When adding a ssh remote make a similar offer. +* Add a UI to add an arbitrary git remote with encryption. + Let the user paste in the url to an empty remote they have, + which could be to eg github. (In most cases this won't be used for + annexed content..) +* When the user has no gpg key, prompt to set one up. (Securely!) +* Maybe have an interface to add another gpg key that can access the gcrypt + repo. Note that this will need to re-encrypt and re-push the whole + git history. |