Migrate to SHA-256 and implicit repo ID (PARTIAL REPO FORMAT CHANGE)
* local, rsync, ssh, sftp repositories are still compatible * gitception/git backend repositories are not compatible and need to be deleted and recreated * Put manifest in a static location, so we don't need #fragment in the URL * Record repository ID for each remote, and warn if it changes. * Use SHA-256 by default but allow reading SHA-224-identified packfiles * The URL #fragment identifies branch to use when using the git backend
This commit is contained in:
parent
4d28d8fe4d
commit
14da0a4d33
2 changed files with 110 additions and 72 deletions
47
README.rst
47
README.rst
|
@ -44,18 +44,15 @@ Quickstart
|
|||
git remote add cryptremote gcrypt::rsync://example.com:repo
|
||||
git push cryptremote master
|
||||
> gcrypt: Setting up new repository
|
||||
> gcrypt: Repository URL is gcrypt::rsync://example.com:repo#KNBr0wKzct52
|
||||
> gcrypt: (configuration for cryptremote updated)
|
||||
> gcrypt: Repository ID is :SHA256:3a29d035adf234af7e[... ]
|
||||
> [ more lines .. ]
|
||||
> To gcrypt::[...]
|
||||
> * [new branch] master -> master
|
||||
|
||||
* Share the updated Repository URL with all participants.
|
||||
|
||||
(The generated Repository URL is not secret, it only exists to ensure
|
||||
that two repositories signed by the same user can not be maliciously
|
||||
switched around. It incidentally allows multiple repositories to all
|
||||
share location.)
|
||||
(The generated Repository id is not secret, it only exists to ensure
|
||||
that two repositories signed by the same user can be distinguished.
|
||||
You will see a warning if the remote repository ID changes, which will
|
||||
only happen if the remote was re-created or switched out.)
|
||||
|
||||
Design Goals
|
||||
............
|
||||
|
@ -98,10 +95,12 @@ Examples
|
|||
How to use a git backend::
|
||||
|
||||
# notice that the target repo must already exist and its
|
||||
# `master` branch will be overwritten!
|
||||
git remote add gitcrypt gcrypt::git@example.com:repo
|
||||
# `next` branch will be overwritten!
|
||||
git remote add gitcrypt gcrypt::git@example.com:repo#next
|
||||
git push gitcrypt HEAD
|
||||
|
||||
The URL fragment (`#next` here) indicates which branch is used.
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
|
@ -112,20 +111,20 @@ Repository Format
|
|||
|
||||
EncSign(X) is sign+encrypt to a PGP key holder
|
||||
Encrypt(K,X) is symmetric encryption
|
||||
Hash(X) is SHA-224
|
||||
Hash(X) is SHA-256
|
||||
|
||||
B: branch list
|
||||
L: list of the hash (Hi) and key (Ki) for each packfile
|
||||
R: Hash(Repository ID)
|
||||
R: repository id
|
||||
|
||||
Store Manifest as EncSign(B || L || R) in filename R
|
||||
Store Manifest as EncSign(B || L || R)
|
||||
Store each packfile P as P' = Encrypt(Ki, P) in filename Hi
|
||||
where Hi = Hash(P') and Ki is a random string
|
||||
|
||||
To read the repository
|
||||
|
||||
decrypt+verify Manifest using private key -> (B, L, R)
|
||||
verify R matches Hash(Requested Repository ID)
|
||||
warn if R does not match saved repository id for this remote
|
||||
for Hi, Ki in L:
|
||||
download file Hi from the server -> P'
|
||||
verify Hash(P') matches Hi
|
||||
|
@ -138,14 +137,13 @@ Manifest file
|
|||
|
||||
::
|
||||
|
||||
$ gpg -d < 5a191cea8c1021a95d813c4007c14f2cc987a40880c2f669430f1916
|
||||
b4a4a39365d19282810c19d0f3f24d04dd2d179f refs/tags/version1
|
||||
1d323ddadf4cf1d80fced447e637ab3766b168b7 refs/heads/master
|
||||
pack :SHA224:cfdf36515e0d0820554fe5fd9f00a4bee17bcf88ec8a752d851c46ee \
|
||||
Rc+j8Nv6GOW3mBhWOx6W6jjz3BTX7B6XIJ6RYI+P4TEy
|
||||
pack :SHA224:a43ccd208d3bd2ea582dbd5407cb8ed6e18b150b1da25c806115eaa5 \
|
||||
UXR3/R7awFCUJWYdzXzrlkk7E2Acxq/Y4EfEcd62AwGG
|
||||
repo :SHA224:5a191cea8c1021a95d813c4007c14f2cc987a40880c2f669430f1916 1
|
||||
$ gpg -d 91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
|
||||
542051c7cd152644e4995bda63cc3ddffd635958 refs/heads/next
|
||||
3c9e76484c7596eff70b21cbe58408b2774bedad refs/heads/master
|
||||
pack :SHA256:f2ad50316fbca42c553810aec3709c24974585ec1b34aae77d5cd4ba67092dc4 z8YoAnFpMlWPIYG8wo1adewd4Fp7Fo3PkI2mND49P1qm
|
||||
pack :SHA256:a6e17bb4c042bdfa8e38856ee6d058d0c0f0c575ace857c4795426492f379584 82+k2cbiUn7i2cW0dgXfyX6wXGpvVaQGj5sF59Y8my5W
|
||||
keep :SHA256:f2ad50316fbca42c553810aec3709c24974585ec1b34aae77d5cd4ba67092dc4 1
|
||||
repo :SHA256:ef8e52a7ea96761f713c14caa7190b5f3b55ff87ffe091cab40f7cbe1d3b5b96
|
||||
|
||||
Each item extends until newline, and matches one of the following forms:
|
||||
|
||||
|
@ -158,8 +156,8 @@ Each item extends until newline, and matches one of the following forms:
|
|||
`keep :<hashtype>:<hash> <generation>`
|
||||
Packfile hash and its repack generation
|
||||
|
||||
`repo :<hashtype>:<hash> <version>`
|
||||
The hash of the repository id.
|
||||
`repo :<hashtype>:<hash>`
|
||||
The repository id
|
||||
|
||||
`extn <name> ...`
|
||||
Extension field, preserved but unused.
|
||||
|
@ -168,7 +166,6 @@ Each item extends until newline, and matches one of the following forms:
|
|||
Yet to be Implemented
|
||||
.....................
|
||||
|
||||
+ Repacking the remote repository
|
||||
+ Some kind of simple keyring management
|
||||
|
||||
See Also
|
||||
|
|
|
@ -16,9 +16,10 @@ Gref="refs/gcrypt/gitception$GITCEPTION"
|
|||
Gref_rbranch="refs/heads/master"
|
||||
Repoid=
|
||||
Packkey_bytes=33 # 33 random bytes for passphrase, still compatible if changed
|
||||
Hashtype=SHA224 # incompatible if changed
|
||||
Packpfx="pack :${Hashtype}:"
|
||||
Keeppfx="keep :${Hashtype}:"
|
||||
Hashtype=SHA256 # SHA512 SHA384 SHA256 SHA224 supported.
|
||||
Packpat="pack :*:"
|
||||
Manifestfile=91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
|
||||
Urlfrag=
|
||||
|
||||
Branchlist=
|
||||
Packlist=
|
||||
|
@ -68,7 +69,7 @@ splitcolon()
|
|||
prefix_=${1%%:*}
|
||||
suffix_=${1#*:}
|
||||
}
|
||||
repoidstr() { xecho "repo :${Hashtype}:$Repoid 1"; }
|
||||
repoidstr() { xecho "repo $Repoid"; }
|
||||
|
||||
## gitception part
|
||||
# Fetch giturl $1, file $2
|
||||
|
@ -77,7 +78,7 @@ gitception_get()
|
|||
# Take care to preserve FETCH_HEAD
|
||||
local ret_=: obj_id= f_head="$GIT_DIR/FETCH_HEAD"
|
||||
[ -e "$f_head" ] && command mv -f "$f_head" "$f_head.$$~" || :
|
||||
git fetch -q -f "$1" "$Gref_rbranch:$Gref" 2>/dev/tty >/dev/null &&
|
||||
git fetch -q -f "$1" "refs/heads/${Urlfrag:-master}:$Gref" 2>/dev/tty >/dev/null &&
|
||||
obj_id="$(git ls-tree "$Gref" | xgrep -E '\b'"$2"'$' | awk '{print $3}')" &&
|
||||
isnonnull "$obj_id" && git cat-file blob "$obj_id" && ret_=: ||
|
||||
{ ret_=false && : ; }
|
||||
|
@ -180,7 +181,8 @@ PUT_FINAL()
|
|||
then
|
||||
:
|
||||
else
|
||||
git push --quiet -f "${1#gitception://}" "$Gref:$Gref_rbranch"
|
||||
git push --quiet -f "${1#gitception://}" \
|
||||
"$Gref:refs/heads/${Urlfrag:-master}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -285,14 +287,17 @@ genkey()
|
|||
gpg --armor --gen-rand 1 "$1"
|
||||
}
|
||||
|
||||
pack_hash()
|
||||
gpg_hash()
|
||||
{
|
||||
local hash_=
|
||||
hash_=$(gpg --with-colons --print-md "$Hashtype" | tr A-F a-f)
|
||||
hash_=$(gpg --with-colons --print-md "$1" | tr A-F a-f)
|
||||
hash_=${hash_#:*:}
|
||||
xecho "${hash_%:}"
|
||||
}
|
||||
|
||||
pack_hash() { gpg_hash "$Hashtype"; }
|
||||
|
||||
|
||||
# Pass the branch/ref by pipe to git
|
||||
safe_git_rev_parse()
|
||||
{
|
||||
|
@ -306,19 +311,15 @@ make_new_repo()
|
|||
echo_info "Setting up new repository"
|
||||
PUTREPO "$URL"
|
||||
|
||||
# We need a relatively short ID for URL+REPO
|
||||
# The manifest will be stored at pack_hash($urlid_)
|
||||
# Needed assumption: the same user should have no duplicate urlid_
|
||||
# For now, we arbitrarily use 9 random bytes (72 bits)
|
||||
urlid_=$(genkey 9 | tr '+/' '-_')
|
||||
Repoid=$(xecho_n "$urlid_" | pack_hash)
|
||||
# Needed assumption: the same user should have no duplicate Repoid
|
||||
Repoid=":${Hashtype}:$(genkey 64 | pack_hash)"
|
||||
iseq "${NAME#gcrypt::}" "$URL" || {
|
||||
git config "remote.$NAME.url" "gcrypt::$URL#$urlid_"
|
||||
git config "remote.$NAME.gcrypt-id" "$Repoid"
|
||||
fix_config=1
|
||||
}
|
||||
echo_info "Repository URL is" "gcrypt::$URL#$urlid_"
|
||||
echo_info "Repository ID is $Repoid"
|
||||
Extension_list=$(xecho "extn comment")
|
||||
isnull "$fix_config" || echo_info "(configuration for $NAME updated)"
|
||||
#isnull "$fix_config" || echo_info "(configuration for $NAME updated)"
|
||||
}
|
||||
|
||||
|
||||
|
@ -365,7 +366,7 @@ read_config()
|
|||
|
||||
ensure_connected()
|
||||
{
|
||||
local manifest_= rcv_repoid= url_id=
|
||||
local manifest_= rcv_repoid= r_name=
|
||||
|
||||
if isnonnull "$Did_find_repo"
|
||||
then
|
||||
|
@ -374,24 +375,36 @@ ensure_connected()
|
|||
Did_find_repo=no
|
||||
read_config
|
||||
|
||||
iseq "${NAME#gcrypt::}" "$URL" || r_name=$NAME
|
||||
|
||||
# Fixup ssh:// -> rsync://
|
||||
if isurl ssh "$URL"; then
|
||||
URL="rsync://${URL#ssh://}"
|
||||
fi
|
||||
|
||||
# split out Repo ID from URL
|
||||
url_id=${URL##*"#"}
|
||||
isnoteq "$url_id" "$URL" || {
|
||||
url_id=${URL##*/"G."}
|
||||
isnoteq "$url_id" "$URL" || return 0
|
||||
URL=${URL%/"G.$url_id"}
|
||||
# Find the URL fragment
|
||||
Urlfrag=${URL##*"#"}
|
||||
isnoteq "$Urlfrag" "$URL" || Urlfrag=
|
||||
URL=${URL%"#$Urlfrag"}
|
||||
|
||||
# manifestfile -- sha224 hash if we can, else the default location
|
||||
if isurl sftp "$URL" || islocalrepo "$URL" || isurl rsync "$URL"
|
||||
then
|
||||
# not for gitception
|
||||
isnull "$Urlfrag" || Manifestfile=$(xecho_n "$Urlfrag" | gpg_hash SHA224)
|
||||
fi
|
||||
|
||||
Repoid=
|
||||
isnull "$r_name" || {
|
||||
Repoid=$(git config "remote.$r_name.gcrypt-id" || :)
|
||||
}
|
||||
URL=${URL%"#$url_id"}
|
||||
Repoid=$(xecho_n "$url_id" | pack_hash)
|
||||
|
||||
|
||||
TmpManifest_Enc="$Localdir/tmp_manifest.$$"
|
||||
GET "$URL" "$Repoid" "$TmpManifest_Enc" 2>/dev/null ||
|
||||
echo_die "Repository not found: $url_id at $URL"
|
||||
GET "$URL" "$Manifestfile" "$TmpManifest_Enc" 2>/dev/null || {
|
||||
echo_info "Repository not found: $URL"
|
||||
return 0
|
||||
}
|
||||
|
||||
Did_find_repo=yes
|
||||
echo_info "Decrypting manifest"
|
||||
|
@ -401,21 +414,50 @@ ensure_connected()
|
|||
rm -f "$TmpManifest_Enc"
|
||||
|
||||
Branchlist=$(xecho "$manifest_" | xgrep -E '^[0-9a-f]{40} ')
|
||||
Packlist=$(xecho "$manifest_" | xgrep "^$Packpfx")
|
||||
Keeplist=$(xecho "$manifest_" | xgrep "^keep")
|
||||
Packlist=$(xecho "$manifest_" | xgrep "^pack ")
|
||||
Keeplist=$(xecho "$manifest_" | xgrep "^keep ")
|
||||
Extension_list=$(xecho "$manifest_" | xgrep "^extn ")
|
||||
rcv_repoid=$(xecho "$manifest_" | xgrep "^repo ")
|
||||
iseq "$(repoidstr)" "$rcv_repoid" || echo_die "Repository id mismatch!"
|
||||
|
||||
rcv_repoid=${rcv_repoid#repo }
|
||||
rcv_repoid=${rcv_repoid% *}
|
||||
if isnull "$Repoid"
|
||||
then
|
||||
echo_info "Remote repo ID is $rcv_repoid"
|
||||
Repoid=$rcv_repoid
|
||||
elif isnoteq "$rcv_repoid" "$Repoid"
|
||||
then
|
||||
echo_info "WARNING:"
|
||||
echo_info "WARNING: Remote repository ID has changed!"
|
||||
echo_info "WARNING: to $rcv_repoid"
|
||||
echo_info "WARNING:"
|
||||
Repoid=$rcv_repoid
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
|
||||
isnull "$r_name" || {
|
||||
git config "remote.$r_name.gcrypt-id" "$rcv_repoid"
|
||||
}
|
||||
}
|
||||
|
||||
fetch_decrypt_pack()
|
||||
{
|
||||
local key_= rcv_id=
|
||||
GET "$URL" "$1" "$TmpPack_Encrypted" &&
|
||||
rcv_id=$(pack_hash < "$TmpPack_Encrypted") &&
|
||||
iseq "$rcv_id" "$1" ||
|
||||
echo_die "Packfile $1 does not match digest!"
|
||||
key_=$(xecho "$Packlist" | grep "$1" | cut -f 3 -d ' ')
|
||||
local key_= rcv_id= htype_= pack_= hfunc_=
|
||||
splitcolon "${1#pack :}"
|
||||
htype_=$prefix_
|
||||
pack_=$suffix_
|
||||
|
||||
if isnoteq "$htype_" SHA256 && isnoteq "$htype_" SHA224 &&
|
||||
isnoteq "$htype_" SHA384 && isnoteq "$htype_" SHA512
|
||||
then
|
||||
echo_die "Packline malformed: $1"
|
||||
fi
|
||||
GET "$URL" "$pack_" "$TmpPack_Encrypted" &&
|
||||
rcv_id=$(gpg_hash "$htype_" < "$TmpPack_Encrypted") &&
|
||||
iseq "$rcv_id" "$pack_" ||
|
||||
echo_die "Packfile $pack_ does not match digest!"
|
||||
key_=$(xecho "$Packlist" | grep "$pack_" | cut -f 3 -d ' ')
|
||||
DECRYPT "$key_" < "$TmpPack_Encrypted"
|
||||
}
|
||||
|
||||
|
@ -462,8 +504,8 @@ repack_if_needed()
|
|||
then
|
||||
continue
|
||||
fi
|
||||
pack_=${packline_#"$Packpfx"}
|
||||
fetch_decrypt_pack "$pack_" |
|
||||
pack_=${packline_#$Packpat}
|
||||
fetch_decrypt_pack "$packline_" |
|
||||
git index-pack -v --stdin "$Localdir/pack/${pack_}.pack" >/dev/null
|
||||
done
|
||||
key_=$(genkey "$Packkey_bytes")
|
||||
|
@ -483,8 +525,8 @@ repack_if_needed()
|
|||
fi
|
||||
|
||||
pack_id=$(pack_hash < "$TmpPack_Encrypted")
|
||||
Packlist=$(append "$Packlist" "$Packpfx$pack_id $key_")
|
||||
Keeplist=$(append "$Keeplist" "$Keeppfx$pack_id 1")
|
||||
Packlist=$(append "$Packlist" "pack :${Hashtype}:$pack_id $key_")
|
||||
Keeplist=$(append "$Keeplist" "keep :${Hashtype}:$pack_id 1")
|
||||
rm -r -f "$Localdir/pack"
|
||||
did_repack=yes
|
||||
}
|
||||
|
@ -542,11 +584,10 @@ do_fetch()
|
|||
xecho "$pneed_" | while read packline_
|
||||
do
|
||||
isnonnull "$packline_" || continue
|
||||
pack_=${packline_#"$Packpfx"}
|
||||
fetch_decrypt_pack "$pack_" |
|
||||
fetch_decrypt_pack "$packline_" |
|
||||
git index-pack -v --stdin >/dev/null
|
||||
# add to local pack list
|
||||
xecho "$Packpfx$pack_" >> "$Localdir/have_packs$GITCEPTION"
|
||||
xecho "${packline_}" >> "$Localdir/have_packs$GITCEPTION"
|
||||
done
|
||||
|
||||
rm -f "$TmpPack_Encrypted"
|
||||
|
@ -614,7 +655,7 @@ EOF
|
|||
|
||||
if isnoteq "$did_repack" yes
|
||||
then
|
||||
Packlist=$(append "$Packlist" "$Packpfx$pack_id $key_")
|
||||
Packlist=$(append "$Packlist" "pack :${Hashtype}:$pack_id $key_")
|
||||
fi
|
||||
# else, repack rewrote Packlist
|
||||
|
||||
|
@ -639,14 +680,14 @@ EOF
|
|||
rm -f "$TmpObjlist"
|
||||
|
||||
# Upload manifest
|
||||
PUT "$URL" "$Repoid" "$TmpManifest_Enc"
|
||||
PUT "$URL" "$Manifestfile" "$TmpManifest_Enc"
|
||||
|
||||
# Delete packs
|
||||
if isnonnull "$Packlist_delete"; then
|
||||
REMOVE "$URL" "$(xecho "$Packlist_delete" | while read packline_
|
||||
do
|
||||
isnonnull "$packline_" || continue
|
||||
pack_=${packline_#"$Packpfx"}
|
||||
pack_=${packline_#$Packpat}
|
||||
xecho "$pack_"
|
||||
done)"
|
||||
fi
|
||||
|
|
Loading…
Reference in a new issue