]> Nutra Git (v1) - gamesguru/git-remote-gcrypt.git/commitdiff
Use manifest file for master key, branches and packs (REPO FORMAT CHANGE)
authorroot <root@localhost>
Thu, 14 Feb 2013 00:00:00 +0000 (00:00 +0000)
committerroot <root@localhost>
Thu, 14 Feb 2013 00:00:00 +0000 (00:00 +0000)
README
git-remote-gcrypt

diff --git a/README b/README
index 99525815a06c0c0e984609e18156dba1cfddf59c..74b61dfa15dc165595a8897bb7dced030c33787c 100644 (file)
--- a/README
+++ b/README
@@ -39,28 +39,23 @@ CONFIGURATION
  + NOTE: We use the user's gnupg configuration for `cipher-algo` and so on!
          Configure your gnupg to use strong crypto -- see `man gpg`.
 
- + Set `git config gcrypt.signmanifest 1` to also sign the manifest (the
-   list of branches and packfiles) when pushing. This is optional and
-   using signed git tags might be an alternative.
- + Set `git config gcrypt.requiresign 1` to fail and stop if no valid
-   signature is found on the manifest.
 
 REPOSITORY FORMAT
 
- + The masterkey is first signed, then encrypted using `gpg -e` with
-   hidden recipients and stored on the remote.
- + The manifest contains the list of branches and packfiles, and an
-   optional signature
+ + The manifest is signed+encrypted using `gpg -se` (with hidden recipients)
+   and stored on the remote.
+ + The manifest contains the list of branches and packfiles.
 
     $ cd MyCryptedRemote
     $ ls 
-    -rw--  11K 00ef27cc2c5b76365e1a46479ed7429e16572c543cdff0a8bf745c7c
-    -rw--  41K b934d8d6c0f48e71b9d7a4d5ea56f024a9bed4f6f2c6f8e688695bee
-    -rw--  577 manifest
-    -rw-- 1.3K masterkey
+    -rw-- 11K 00ef27cc2c5b76365e1a46479ed7429e16572c543cdff0a8bf745c7c
+    -rw-- 41K b934d8d6c0f48e71b9d7a4d5ea56f024a9bed4f6f2c6f8e688695bee
+    -rw-- 577 manifest
     
-    $ gpg -d masterkey | gpg -d | gpg --passphrase-fd 0 -d manifest
+    $ gpg -d manifest
+    T+pCUr/1FxbBC93ABIiIgG36EgqaxvgdNYjdmRSueGkgGETc4Qs7di+/yIsq2R5GysiqFaR0 \
+    bGSWf9omsoAH84hmED/kR/ZQiOGT/vg2Pg7CGI0xzdlW9GQjeFBAo4vsDDDBxrn5L7F9E532 \
+    LOnnPLSIZD7BpmyY/oZiXoP5Vlw=
     b4a4a39365d19282810c19d0f3f24d04dd2d179f refs/tags/something
     1d323ddadf4cf1d80fced447e637ab3766b168b7 refs/heads/master
     pack :SHA224:00ef27cc2c5b76365e1a46479ed7429e16572c543cdff0a8bf745c7c
@@ -69,21 +64,24 @@ REPOSITORY FORMAT
 
  + Protocol sketch
 
-   gpg -c is symmetric encryption, for example AES
-   gpg -e is encrypting to a PGP key holder
-   gpg -s adds a signature
+   EncSign(X) is sign+encrypting to a PGP key holder
+   Encrypt(K,X) is symmetric encryption
 
-   master key M, generated once, 128 bytes
-   file `masterkey' contains   cat M | gpg -s | gpg -e > `masterkey'
-   manifest and packfiles are encrypted    cat FILE | gpg -c --passphrase M
+   K: master key, generated once, 128 bytes
+   B: branch list
+   L: list of packfiles and hashes
+   
+   Store Manifest as EncSign(K || B || L)
+   Each packfile P is stored as P' = Encrypt(K,P) and named SHA224(P')
+   L is the list of names of P'.
 
-   To read repository
-   decrypt  cat `masterkey' | gpg -d | gpg --verify > M
-   decrypt  cat FILE.crypt  | gpg -d --passphrase M
 
-   The masterkey is decrypted and its signature is verified before
-   reading or writing of any other file. Only packs mentioned in `manifest`
-   are downloaded.
-   Pack hashes are verified when fetched. The filename is simply the hash
-   of the packfile.
+   To read the repository
+
+   decrypt+verify Manifest using private key -> (K, B, L)
+   for each packfile P' in L:
+       verify  P' matches its hash
+       decrypt P' using K -> P -> open P with git
+
+   Only packs mentioned in L are downloaded.
 
index 7f8df61c2216e5ba47ff16be0318bb788199681c..cb78ca3e919a92efa2ce2ad4d1dd36f027164391 100755 (executable)
@@ -9,22 +9,12 @@
 #set -x
 set -e
 
-genkey()
-{
-       gpg --armor --gen-rand 1 128 | tr -d \\n
-}
-
-pack_hash()
-{
-       local HASH=$(gpg --with-colons --print-md SHA224 | tr A-F a-f)
-       HASH=${HASH#:*:}; printf "%s" "${HASH%:}"
-}
-
-LOCALDIR="${GIT_DIR:-.git}/remote-gcrypt"
 DID_FIND_REPO=  # yes for connected, no for no repo
-PACKPFX="pack :SHA224:"
+LOCALDIR="${GIT_DIR:-.git}/remote-gcrypt"
 export GITCEPTION="$GITCEPTION+" # Reuse $GREF except when stacked
-GREF="refs/gcrypt/gitception.$GITCEPTION"
+GREF="refs/gcrypt/gitception$GITCEPTION"
+MANIFESTFILE=5e4a937219be20f8a9a16ae7b35a83db0c16ce501d27b231dbad6586
+PACKPFX="pack :SHA224:"
 
 isurl() { test -z "${2%%$1://*}" ; }
 
@@ -74,9 +64,9 @@ update_tree()
 # depends on previous GET to set $GREF and depends on PUT_FINAL later
 gitception_put()
 {
-       OBJID=$(git hash-object -w --stdin) && \
+       OBJID=$(git hash-object -w --stdin) &&
                TREEID=$(update_tree "$GREF" "$2" "$OBJID") &&
-               COMMITID=$(anon_commit "$TREEID" -m "x") && \
+               COMMITID=$(anon_commit "$TREEID" -m "x") &&
                git update-ref "$GREF" "$COMMITID"
 }
 ## end gitception
@@ -157,59 +147,53 @@ CLEAN_FINAL()
 
 ENCRYPT()
 {
-       # Security protocol:
-       # Symmetric encryption using the long MASTERKEY.
-       (printf "%s" "$MASTERKEY" | \
+       (printf "%s" "$MASTERKEY" |
                gpg --batch --force-mdc --compress-algo none \
                        --passphrase-fd 0 --output - -c /dev/fd/3) 3<&0
 }
 
 DECRYPT()
 {
-       (printf "%s" "$MASTERKEY" | \
+       (printf "%s" "$MASTERKEY" |
                gpg -q --batch --no-default-keyring --secret-keyring /dev/null \
                        --keyring /dev/null \
                        --passphrase-fd 0 --output - -d /dev/fd/3) 3<&0
 }
 
-CLEARSIGN()
+# Encrypt to recipients $1
+PRIVENCRYPT()
 {
-       if [ "$CONF_SIGN_MANIFEST" = "true" ]
-       then
-               echo_info "Requesting manifest signature for push"
-               gpg --output - --clearsign
-       else
-               cat
-       fi
+       gpg --no-default-keyring --keyring "$CONF_KEYRING" \
+               --compress-algo none -se $1
 }
 
-# Require both gpg success and status word $1
-gpg_check_status()
+PRIVDECRYPT()
 {
        local STATUS
-       local ARG
-       ARG=$1 ; shift;
-       STATUS=$(gpg --status-fd 3 "$@" 3>&1 1>&4) 4>&1 &&
-               printf "%s" "$STATUS" | grep "^\[GNUPG:\] $ARG " >/dev/null
+       STATUS=$(gpg --no-default-keyring --keyring "$CONF_KEYRING" \
+               --status-fd 3 -q -d 3>&1 1>&4) 4>&1 &&
+       printf "%s" "$STATUS" | grep "^\[GNUPG:\] ENC_TO " >/dev/null &&
+       (printf "%s" "$STATUS" | grep "^\[GNUPG:\] GOODSIG " >/dev/null || {
+               echo_info "Failed to verify manifest signature!" && return 1
+       })
 }
 
-VERIFYSIGN()
+genkey()
 {
-       gpg_check_status "GOODSIG" -q --batch --no-default-keyring \
-               --secret-keyring /dev/null --keyring "$CONF_KEYRING" -d
+       gpg --armor --gen-rand 1 128 | tr -d \\n
 }
 
-PRIVDECRYPT()
+pack_hash()
 {
-       gpg_check_status "ENC_TO" -q -d
+       local HASH=$(gpg --with-colons --print-md SHA224 | tr A-F a-f)
+       HASH=${HASH#:*:}; printf "%s" "${HASH%:}"
 }
 
 
 # Append $2 to $1 with a newline separator
 append()
 {
-       [ -n "$1" ] && printf "%s\n" "$1" || :
-       printf "%s\n" "$2"
+       [ -z "$1" ] || printf "%s\n" "$1" && printf "%s\n" "$2"
 }
 
 xgrep() { command grep "$@" || : ; }
@@ -217,18 +201,12 @@ sort_C() { LC_ALL=C command sort "$@"; }
 tac() { sed '1!G;h;$!d'; }
 echo_info() { echo "gcrypt:" "$@" >&2; }
 
-make_new_repo()
+check_recipients()
 {
-       # Security protocol:
-       # The MASTERKEY is encrypted to all RECIPIENTS. The key is a long
-       # ascii-encoded string used for symmetric encryption with GnuPG.
-       local RECIPIENTS
-       local KEYSIGN
-       echo_info "Setting up new repository at $URL"
        RECIPIENTS="$(gpg --no-default-keyring --keyring "$CONF_KEYRING" \
-               --with-colons -k | xgrep ^pub | cut -f5 -d:)"
+               --with-colons -k | xgrep ^pub | cut -f5 -d: | tr '\n' ' ')"
        # Split recipients by space, example "a b c" =>  -R a -R b -R c
-       RECIPIENTS=$(printf "%s" $RECIPIENTS | sed -e 's/\([^ ]\+\)/-R &/g')
+       RECIPIENTS=$(printf "%s" "$RECIPIENTS" | sed -e 's/\([^ ]\+\)/-R &/g')
        if [ -z "$RECIPIENTS" ]
        then
                echo_info "You must configure a keyring for the repository."
@@ -237,94 +215,52 @@ make_new_repo()
                echo_info "  git config gcrypt.keyring <path-to-keyring>"
                exit 1
        fi
-       PUTREPO "$URL"
-       echo_info "Generating master key"
-       echo_info "Requesting master key signature"
-       MASTERKEY="$(genkey)"
-       KEYSIGN=$(printf "%s\n" "$MASTERKEY" | gpg --output - --clearsign)
-       TMPMASTERKEY_ENC="$LOCALDIR/masterenc.$$"
-       trap 'rm -f "$TMPMASTERKEY_ENC"' EXIT
-       echo_info "Encrypting masterkey to \"$RECIPIENTS\""
-       printf "%s" "$KEYSIGN" | gpg --batch --no-default-keyring \
-               --secret-keyring /dev/null --keyring "$CONF_KEYRING" \
-               --compress-algo none -e $RECIPIENTS > "$TMPMASTERKEY_ENC"
-       PUT "$URL" masterkey < "$TMPMASTERKEY_ENC"
-       rm -f "$TMPMASTERKEY_ENC"
-       trap EXIT
 }
 
-get_masterkey()
+make_new_repo()
 {
-       # The master key and its clearsigned versions are safe to keep
-       # as text in variables
-       local MASTERKEYDEC
-       TMPMASTERKEY_ENC="$LOCALDIR/masterenc.$$"
-       trap 'rm -f "$TMPMASTERKEY_ENC"' EXIT
-       GET "$URL" masterkey 2>/dev/null > "$TMPMASTERKEY_ENC" || return 0
-       MASTERKEYDEC=$(PRIVDECRYPT < "$TMPMASTERKEY_ENC") || {
-               echo_info "Decryption of master key failed!"
-               exit 1
-       }
-       echo_info "Verifying master key signature"
-       printf "%s" "$MASTERKEYDEC" | VERIFYSIGN || {
-               echo_info "Failed to verify master key signature!"
-               echo_info "Using keyring $CONF_KEYRING"
-               if [ "$CONF_KEYRING" = "/dev/null" ] ; then
-               echo_info "Please configure gcrypt.keyring"
-               fi
-               exit 1
-       }
-       rm -f "$TMPMASTERKEY_ENC"
-       trap EXIT
+       echo_info "Setting up new repository at $URL"
+       PUTREPO "$URL"
+       echo_info "Generating master key"
+       MASTERKEY="$(genkey)"
 }
 
 
 read_config()
 {
-       CONF_SIGN_MANIFEST=$(git config --bool gcrypt.signmanifest || :)
-       CONF_REQUIRE_SIGN=$(git config --bool gcrypt.requiresign || :)
        CONF_KEYRING=$(git config --path gcrypt.keyring || printf "/dev/null")
 }
 
 ensure_connected()
 {
-       local MANIFESTDATA
-       local STRIPDATA
+       local MANIFEST
 
        if [ -n "$DID_FIND_REPO" ]
        then
                return
        fi
-       DID_FIND_REPO=yes
+       DID_FIND_REPO=no
        read_config
 
-       MASTERKEY="$(get_masterkey)"
-       if [ -z "$MASTERKEY" ]
-       then
-               DID_FIND_REPO=no
-               return
-       fi
-       MANIFESTDATA="$(GET "$URL" manifest | DECRYPT)"
-       if [ "$CONF_REQUIRE_SIGN" = true -o -z "${MANIFESTDATA##-----BEGIN*}" ]
-       then
-               # Use gpg to verify and strip the signature
-               echo_info "Verifying manifest signature"
-               STRIPDATA="$(printf "%s" "$MANIFESTDATA" | VERIFYSIGN || {
-                       echo_info "WARNING: Failed to verify manifest signature"
-                       echo_info "WARNING: Using keyring $CONF_KEYRING"
-                       if [ "$CONF_KEYRING" = "/dev/null" ] ; then
-                       echo_info "WARNING: Please configure gcrypt.keyring"
-                       fi
-                       if [ "$CONF_REQUIRE_SIGN" = "true" ] ; then
-                       echo_info "Exiting per gcrypt.requiresign" && exit 1
-                       fi
-                       }
-               )"
-               [ -n "$STRIPDATA" ] && MANIFESTDATA=$STRIPDATA || :
-       fi
-       [ -n "$MANIFESTDATA" ] || exit 1
-       BRANCHLIST=$(printf "%s\n" "$MANIFESTDATA" | xgrep -E '^[0-9a-f]{40}')
-       PACKLIST=$(printf "%s\n" "$MANIFESTDATA" | xgrep "^$PACKPFX")
+       TMPMANIFEST_ENC="$LOCALDIR/manifest.$$"
+       trap 'rm -f "$TMPMANIFEST_ENC"' EXIT
+       GET "$URL" "$MANIFESTFILE" 2>/dev/null > "$TMPMANIFEST_ENC" || return 0
+
+       DID_FIND_REPO=yes
+       echo_info "Decrypting manifest"
+       MANIFEST=$(PRIVDECRYPT < "$TMPMANIFEST_ENC") && [ -n "$MANIFEST" ] || {
+               echo_info "Failed to decrypt manifest!"
+               echo_info "Using keyring $CONF_KEYRING"
+               if [ "$CONF_KEYRING" = "/dev/null" ] ; then
+                       echo_info "NOTE: Please configure gcrypt.keyring"
+               fi
+               exit 1
+       }
+       rm -f "$TMPMANIFEST_ENC"
+       trap EXIT
+       MASTERKEY=$(printf "%s\n" "$MANIFEST" | head -n 1)
+       BRANCHLIST=$(printf "%s\n" "$MANIFEST" | xgrep -E '^[0-9a-f]{40} ')
+       PACKLIST=$(printf "%s\n" "$MANIFEST" | xgrep "^$PACKPFX")
 }
 
 do_capabilities()
@@ -392,7 +328,7 @@ do_fetch()
                        echo_info "Packfile $PACK does not match digest!"
                        exit 1
                fi
-               DECRYPT < "$TMPPACK_ENCRYPTED" | \
+               DECRYPT < "$TMPPACK_ENCRYPTED" |
                        git index-pack -v --stdin >/dev/null
                # add to local pack list
                printf "$PACKPFX%s\n" "$PACK">>"$LOCALDIR/have_packs$GITCEPTION"
@@ -416,6 +352,7 @@ do_push()
        local prefix_
        local suffix_
        ensure_connected
+       check_recipients
 
        if [ "$DID_FIND_REPO" = "no" ]
        then
@@ -439,7 +376,8 @@ do_push()
                if [ -n "$prefix_" ]
                then
                        printf "%s " "$prefix_"
-                       printf "%s %s\n" "$(git rev-parse "$prefix_")" "$suffix_" >> "$TMPMANIFEST"
+                       printf "%s %s\n" "$(git rev-parse "$prefix_")" \
+                               "$suffix_" >> "$TMPMANIFEST"
                # else delete
                fi
        done)"
@@ -460,12 +398,27 @@ do_push()
                PUT "$URL" "$PACKID" < "$TMPPACK_ENCRYPTED"
        fi
 
-       # Put new manifest
-       SIGNMANIFEST=$(printf "%s\n%s\n" "$BRANCHLIST" "$PACKLIST" | CLEARSIGN)
-       printf "%s\n" "$SIGNMANIFEST" | ENCRYPT | PUT "$URL" "manifest"
+       rm -f "$TMPPACK_ENCRYPTED"
+       rm -f "$TMPMANIFEST"
+       rm -f "$TMPOBJLIST"
+       trap EXIT
+
+       # Update manifest
+       echo_info "Encrypting manifest to \"$RECIPIENTS\""
+       echo_info "Requesting manifest key signature"
+
+       TMPMANIFEST_ENC="$LOCALDIR/manifest.$$"
+       trap 'rm -f "$TMPMANIFEST_ENC"' EXIT
+
+       printf "%s\n%s\n%s\n" "$MASTERKEY" "$BRANCHLIST" "$PACKLIST" |
+               PRIVENCRYPT "$RECIPIENTS" > "$TMPMANIFEST_ENC"
+       PUT "$URL" "$MANIFESTFILE" < "$TMPMANIFEST_ENC"
 
        PUT_FINAL "$URL"
 
+       rm -f "$TMPMANIFEST_ENC"
+       trap EXIT
+
        # ok all updates (not deletes)
        printf "%s\n" "$1" | while read LINE
        do
@@ -479,19 +432,15 @@ do_push()
                fi
        done
        
-       rm -f "$TMPPACK_ENCRYPTED"
-       rm -f "$TMPMANIFEST"
-       rm -f "$TMPOBJLIST"
-       trap EXIT
        echo 
 }
 
 # Main program, check $URL is supported
 NAME=$1
 URL=$2
-( isurl ssh "$URL" || isurl sftp "$URL" || isurl gitception "$URL" || \
-       test -z ${URL##/*} ) || \
-       { echo_info "Supported URLs: gitception://<giturl>, Absolute path, sftp://, ssh://" ; exit 1 ; }
+( isurl ssh "$URL" || isurl sftp "$URL" ||
+       isurl gitception "$URL" || test -z ${URL##/*} ) || {
+       echo_info "Supported URLs: gitception://<giturl>, Absolute path, sftp://, ssh://" && exit 1 ; }
 
 mkdir -p "$LOCALDIR"