Use a separate symmetric key per packfile (REPO FORMAT CHANGE)

A separate key per pack is simpler and costs us very little; with
repack changes later it will be possible to change keys regularly.
This commit is contained in:
root 2013-02-14 00:00:00 +00:00
parent 1cb9281b45
commit 63d7a7437e
2 changed files with 49 additions and 52 deletions

View file

@ -24,7 +24,7 @@ Example use::
git push cryptremote master git push cryptremote master
> gcrypt: Setting up new repository at ssh://example.com:repo > gcrypt: Setting up new repository at ssh://example.com:repo
> gcrypt: Repository ID is KNBr0wKzct52 > gcrypt: Repository ID is KNBr0wKzct52
> gcrypt: Repository URL is gcrypt::ssh://example.com:repo/G/KNBr0wKzct52 > gcrypt: Repository URL is gcrypt::ssh://example.com:repo/G.KNBr0wKzct52
> gcrypt: (configuration for cryptremote updated) > gcrypt: (configuration for cryptremote updated)
> [ more lines .. ] > [ more lines .. ]
> To gcrypt::[...] > To gcrypt::[...]
@ -70,37 +70,35 @@ Repository Format
Encrypt(K,X) is symmetric encryption Encrypt(K,X) is symmetric encryption
Hash(X) is SHA-224 Hash(X) is SHA-224
K: master key, generated once, 128 bytes
B: branch list B: branch list
L: list of packfile hashes L: list of the hash (Hi) and key (Ki) for each packfile
R: Hash(Repository ID) R: Hash(Repository ID)
Store Manifest as EncSign(K || B || L || R) in filename R Store Manifest as EncSign(B || L || R) in filename R
Each packfile P is stored as P' = Encrypt(K,P) in filename Hash(P') Store each packfile P as P' = Encrypt(Ki, P) in filename Hi
L is the list of Hash(P'). where Hi = Hash(P') and Ki is a random string
To read the repository To read the repository
decrypt+verify Manifest using private key -> (K, B, L, R) decrypt+verify Manifest using private key -> (B, L, R)
verify R matches Hash(Requested Repository ID) verify R matches Hash(Requested Repository ID)
for each entry in L: for Hi, Ki in L:
get the entry from the server -> P' download file Hi from the server -> P'
verify Hash(P') matches the entry in L verify Hash(P') matches Hi
decrypt P' using K -> P -> open P with git decrypt P' using Ki -> P then open P with git
Only packs mentioned in L are downloaded. Only packs mentioned in L are downloaded.
+ The manifest looks like this:: + The manifest looks like this::
$ gpg -d < 9f42017de5cb482e509ff147d54ceeb0413d6379717f3f0db770f00a $ gpg -d < 5a191cea8c1021a95d813c4007c14f2cc987a40880c2f669430f1916
T+pCUr/1FxbBC93ABIiIgG36EgqaxvgdNYjdmRSueGkgGETc4Qs7di+/yIsq2R5GysiqFaR0 \ b4a4a39365d19282810c19d0f3f24d04dd2d179f refs/tags/version1
bGSWf9omsoAH84hmED/kR/ZQiOGT/vg2Pg7CGI0xzdlW9GQjeFBAo4vsDDDBxrn5L7F9E532 \
LOnnPLSIZD7BpmyY/oZiXoP5Vlw=
b4a4a39365d19282810c19d0f3f24d04dd2d179f refs/tags/something
1d323ddadf4cf1d80fced447e637ab3766b168b7 refs/heads/master 1d323ddadf4cf1d80fced447e637ab3766b168b7 refs/heads/master
pack :SHA224:00ef27cc2c5b76365e1a46479ed7429e16572c543cdff0a8bf745c7c pack :SHA224:cfdf36515e0d0820554fe5fd9f00a4bee17bcf88ec8a752d851c46ee Rc+j8\
pack :SHA224:b934d8d6c0f48e71b9d7a4d5ea56f024a9bed4f6f2c6f8e688695bee Nv6GOW3mBhWOx6W6jjz3BTX7B6XIJ6RYI+P4TEyy+X6p2PB/fsBL9la0Tuc
repo 9f42017de5cb482e509ff147d54ceeb0413d6379717f3f0db770f00a pack :SHA224:a43ccd208d3bd2ea582dbd5407cb8ed6e18b150b1da25c806115eaa5 UXR3/\
R7awFCUJWYdzXzrlkk7E2Acxq/Y4EfEcd62AwGGe0o0QxL+s5CwWI/NvMhb
repo :SHA224:5a191cea8c1021a95d813c4007c14f2cc987a40880c2f669430f1916 1
Pieces yet to be Implemented Pieces yet to be Implemented

View file

@ -15,7 +15,10 @@ export GITCEPTION="$GITCEPTION+" # Reuse $Gref except when stacked
Gref="refs/gcrypt/gitception$GITCEPTION" Gref="refs/gcrypt/gitception$GITCEPTION"
Gref_rbranch="refs/heads/master" Gref_rbranch="refs/heads/master"
Repoid= Repoid=
Packpfx="pack :SHA224:" Hashpfx=":SHA224:"
UrlTag="G."
Packpfx="pack $Hashpfx"
Packkey_len=48 # bytes of pack key
# compat/utility functions # compat/utility functions
xecho() xecho()
@ -53,6 +56,7 @@ splitcolon()
prefix_=${1%%:*} prefix_=${1%%:*}
suffix_=${1#*:} suffix_=${1#*:}
} }
repoidstr() { xecho "repo $Hashpfx$Repoid 1"; }
## gitception part ## gitception part
# Fetch giturl $1, file $2 # Fetch giturl $1, file $2
@ -182,18 +186,16 @@ CLEAN_FINAL()
ENCRYPT() ENCRYPT()
{ {
gpg --batch --force-mdc --compress-algo none \ gpg --batch --force-mdc --compress-algo none --passphrase-fd 3 -c 3<<EOF
--passphrase-fd 3 -c 3<<EOF $1
$Masterkey
EOF EOF
} }
DECRYPT() DECRYPT()
{ {
gpg -q --batch --no-default-keyring --secret-keyring /dev/null \ gpg -q --batch --no-default-keyring --secret-keyring /dev/null \
--keyring /dev/null \ --keyring /dev/null --passphrase-fd 3 -d 3<<EOF
--passphrase-fd 3 -d 3<<EOF $1
$Masterkey
EOF EOF
} }
@ -260,7 +262,6 @@ make_new_repo()
local urlid_= fix_config= local urlid_= fix_config=
echo_info "Setting up new repository at $URL" echo_info "Setting up new repository at $URL"
PUTREPO "$URL" PUTREPO "$URL"
Masterkey="$(genkey 128)"
# We need a relatively short ID for URL+REPO # We need a relatively short ID for URL+REPO
# The manifest will be stored at SHA224(urlid_) # The manifest will be stored at SHA224(urlid_)
@ -269,12 +270,12 @@ make_new_repo()
urlid_=$(genkey 9 | tr '/+' 'Zz') urlid_=$(genkey 9 | tr '/+' 'Zz')
Repoid=$(xecho_n "$urlid_" | pack_hash) Repoid=$(xecho_n "$urlid_" | pack_hash)
echo_info "Repository ID is" "$urlid_" echo_info "Repository ID is" "$urlid_"
isnoteq "${NAME#gcrypt::}" "$URL" && { iseq "${NAME#gcrypt::}" "$URL" || {
git config "remote.$NAME.url" "gcrypt::$URL/G/$urlid_" git config "remote.$NAME.url" "gcrypt::$URL/$UrlTag$urlid_"
fix_config=1 fix_config=1
} || : }
echo_info "Repository URL is" "gcrypt::$URL/G/$urlid_" echo_info "Repository URL is" "gcrypt::$URL/$UrlTag$urlid_"
isnonnull "$fix_config" && echo_info "(configuration for $NAME updated)"||: isnull "$fix_config" || echo_info "(configuration for $NAME updated)"
} }
@ -295,10 +296,10 @@ ensure_connected()
read_config read_config
# split out Repoid from URL # split out Repoid from URL
url_id=${URL##*/G/} url_id=${URL##*/"$UrlTag"}
iseq "$url_id" "$URL" && url_id= && return 0 || : isnoteq "$url_id" "$URL" || return 0
URL=${URL%/G/"$url_id"} URL=${URL%/"$UrlTag$url_id"}
Repoid=$(xecho_n "$url_id" | pack_hash) Repoid=$(xecho_n "$url_id" | pack_hash)
TmpManifest_Enc="$Localdir/manifest.$$" TmpManifest_Enc="$Localdir/manifest.$$"
@ -321,12 +322,10 @@ ensure_connected()
rm -f "$TmpManifest_Enc" rm -f "$TmpManifest_Enc"
trap - EXIT trap - EXIT
Masterkey=$(xecho "$manifest_" | head -n 1)
Branchlist=$(xecho "$manifest_" | xgrep -E '^[0-9a-f]{40} ') Branchlist=$(xecho "$manifest_" | xgrep -E '^[0-9a-f]{40} ')
Packlist=$(xecho "$manifest_" | xgrep "^$Packpfx") Packlist=$(xecho "$manifest_" | xgrep "^$Packpfx")
rcv_repoid=$(xecho "$manifest_" | xgrep "^repo ") rcv_repoid=$(xecho "$manifest_" | xgrep "^repo ")
iseq "repo $Repoid" "$rcv_repoid" || iseq "$(repoidstr)" "$rcv_repoid" || echo_die "Repository id mismatch!"
echo_die "Repository id mismatch!"
} }
do_capabilities() do_capabilities()
@ -343,7 +342,7 @@ do_list()
xecho "$Branchlist" | while read line_ xecho "$Branchlist" | while read line_
do do
isnull "$line_" && break || : isnonnull "$line_" || break
obj_id=${line_%% *} obj_id=${line_%% *}
ref_name=${line_##* } ref_name=${line_##* }
echo_git "$obj_id" "$ref_name" echo_git "$obj_id" "$ref_name"
@ -363,7 +362,7 @@ do_fetch()
# The PACK id is the hash of the encrypted git packfile. # The PACK id is the hash of the encrypted git packfile.
# We only download packs mentioned in the encrypted manifest, # We only download packs mentioned in the encrypted manifest,
# and check their digest when received. # and check their digest when received.
local pack_= rcv_id= packline_= pneed_= pboth_= phave_= local pack_= rcv_id= packline_= pneed_= pboth_= phave_= premote_= key_=
ensure_connected ensure_connected
@ -376,15 +375,16 @@ do_fetch()
TmpPack_Encrypted="$Localdir/tmp_pack_ENCRYPTED_.$$" TmpPack_Encrypted="$Localdir/tmp_pack_ENCRYPTED_.$$"
trap 'rm -f "$TmpPack_Encrypted"' EXIT trap 'rm -f "$TmpPack_Encrypted"' EXIT
premote_=$(xecho "$Packlist" | cut -f 1-2 -d ' ')
# Needed packs is Packlist - (phave & Packlist) # Needed packs is Packlist - (phave & Packlist)
# The `+` for $GITCEPTION is pointless but we will be safe for stacking # The `+` for $GITCEPTION is pointless but we will be safe for stacking
phave_="$(cat "$Localdir/have_packs+" 2>/dev/null || :)" phave_="$(cat "$Localdir/have_packs+" 2>/dev/null || :)"
pboth_="$( (xecho "$Packlist"; xecho "$phave_") | sort_C | uniq -d)" pboth_="$( (xecho "$premote_"; xecho "$phave_") | sort_C | uniq -d)"
pneed_="$( (xecho "$Packlist"; xecho "$pboth_") | sort_C | uniq -u)" pneed_="$( (xecho "$premote_"; xecho "$pboth_") | sort_C | uniq -u)"
xecho "$pneed_" | while read packline_ xecho "$pneed_" | while read packline_
do do
isnull "$packline_" && continue || : isnonnull "$packline_" || continue
pack_=${packline_#"$Packpfx"} pack_=${packline_#"$Packpfx"}
rcv_id="$(GET "$URL" "$pack_" | \ rcv_id="$(GET "$URL" "$pack_" | \
tee "$TmpPack_Encrypted" | pack_hash)" tee "$TmpPack_Encrypted" | pack_hash)"
@ -392,7 +392,8 @@ do_fetch()
then then
echo_die "Packfile $pack_ does not match digest!" echo_die "Packfile $pack_ does not match digest!"
fi fi
DECRYPT < "$TmpPack_Encrypted" | key_=$(xecho "$Packlist" | grep "$pack_" | cut -f 3 -d ' ')
DECRYPT "$key_" < "$TmpPack_Encrypted" |
git index-pack -v --stdin >/dev/null git index-pack -v --stdin >/dev/null
# add to local pack list # add to local pack list
xecho "$Packpfx$pack_" >> "$Localdir/have_packs$GITCEPTION" xecho "$Packpfx$pack_" >> "$Localdir/have_packs$GITCEPTION"
@ -410,7 +411,7 @@ do_push()
# Each git packfile is encrypted and then named for the encrypted # Each git packfile is encrypted and then named for the encrypted
# file's hash. The manifest is updated with the pack id. # file's hash. The manifest is updated with the pack id.
# The manifest is encrypted. # The manifest is encrypted.
local remote_has= remote_want= prefix_= suffix_= line_= pack_id= local remote_has= remote_want= prefix_= suffix_= line_= pack_id= key_=
ensure_connected ensure_connected
check_recipients check_recipients
@ -447,16 +448,17 @@ EOF
TmpPack_Encrypted="$Localdir/tmp_pack_ENCRYPTED_.$$" TmpPack_Encrypted="$Localdir/tmp_pack_ENCRYPTED_.$$"
TmpObjlist="$Localdir/tmp_packrevlist.$$" TmpObjlist="$Localdir/tmp_packrevlist.$$"
key_=$(genkey "$Packkey_len")
append "$remote_has" "$remote_want" | append "$remote_has" "$remote_want" |
git rev-list --objects --stdin -- | git rev-list --objects --stdin -- |
tee "$TmpObjlist" | tee "$TmpObjlist" |
git pack-objects --stdout | ENCRYPT > "$TmpPack_Encrypted" git pack-objects --stdout | ENCRYPT "$key_">"$TmpPack_Encrypted"
# Only send pack if we have any objects to send # Only send pack if we have any objects to send
if [ -s "$TmpObjlist" ] if [ -s "$TmpObjlist" ]
then then
pack_id=$(pack_hash < "$TmpPack_Encrypted") pack_id=$(pack_hash < "$TmpPack_Encrypted")
Packlist=$(append "$Packlist" "$Packpfx$pack_id") Packlist=$(append "$Packlist" "$Packpfx$pack_id $key_")
PUT "$URL" "$pack_id" < "$TmpPack_Encrypted" PUT "$URL" "$pack_id" < "$TmpPack_Encrypted"
fi fi
@ -466,15 +468,12 @@ EOF
# Update manifest # Update manifest
echo_info "Encrypting manifest to \"$Recipients\"" echo_info "Encrypting manifest to \"$Recipients\""
echo_info "Requesting manifest key signature" echo_info "Requesting manifest signature"
TmpManifest_Enc="$Localdir/manifest.$$" TmpManifest_Enc="$Localdir/manifest.$$"
trap 'rm -f "$TmpManifest_Enc"' EXIT trap 'rm -f "$TmpManifest_Enc"' EXIT
(xecho "$Masterkey" (xecho "$Branchlist"; xecho "$Packlist"; repoidstr) |
xecho "$Branchlist"
xecho "$Packlist"
xecho "repo $Repoid") |
PRIVENCRYPT "$Recipients" > "$TmpManifest_Enc" PRIVENCRYPT "$Recipients" > "$TmpManifest_Enc"
PUT "$URL" "$Repoid" < "$TmpManifest_Enc" PUT "$URL" "$Repoid" < "$TmpManifest_Enc"