group support for git sigs
This commit is contained in:
parent
b53757b062
commit
a56a9c477e
|
@ -1,16 +1,16 @@
|
||||||
-----BEGIN PGP SIGNATURE-----
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
iQIzBAABCgAdFiEEZ1U/vaRrtxq9LgsLjkeh7DWhVR0FAl+ydC8ACgkQjkeh7DWh
|
iQIzBAABCgAdFiEEZ1U/vaRrtxq9LgsLjkeh7DWhVR0FAl+zF58ACgkQjkeh7DWh
|
||||||
VR09Lg/9F+xu75alg8aXBSw2JJXf90OrZukVi54FoIMoziYbQLPazu8TqvcS9x4M
|
VR2DbQ//fLBe5OUcrT7O4TWguYPOYmQJSNM7NWG2tNS4roiJFaJUPhx0djnEIhqI
|
||||||
5Z9Aw1GJlkNXd02zqLNO41twLAmbfBUjm+FUa3oB6MkyIijjgLk913XjUmy+ZHw1
|
4Ui8FJfXyowHYDyxbtzL165bl2LcjUVlp2+xyTZxZwqh/TGVSqSy0tioepiWiTO3
|
||||||
kvPihdiNFefzQsWAmw+Qvt+S9sXLQ4h/CIc86/Skb61ivoY5V1VR2zIZfUIg4na1
|
f/aVki7j7TkGwi/fPb1t7WpwbGlMxgXDGFsYYZ8+MWi7XQEg72bl+dOHUy8EsDBd
|
||||||
rO3mSWCzO0H7EOCb+ahB716Oal4Byw7AsbQjSIt25Nlqq4l5j7AQnDKHo/tiLNS8
|
jZKfWuc6NcpUwip814WAQBTWQbRrNdyTPTn/jJ974Ba75trEY9RMbMbaDw80Aw98
|
||||||
RNeU5mIjQ3kPOWtSXGYYuxS0VjVap33ZAGsHx6g2NrrO3wbb2BoGsBAHGsFlUetd
|
KoRSo6zDu/xT9UBoxWfRO+aMahtYqKNO+F8PmXFxXGI0Gi3iwciv82H5h3KH52Cx
|
||||||
wq8pbLkA+9HXTvIwR+2llKp0i+2uhPWiD+/SNKVWIqrRWr6ySWDjXHjut5cP74zo
|
LHAeUApApI5xD0A7s/UfOei48ed34XiSymbJG6ppIwaEsXNIgbe3qhdx4YvGQ9X0
|
||||||
lEE4qBweT4PuW9YmEIvQaKHUKeTvP/sWYinKizVtM3jmbcFfTAWqdMeCc+V0kZhb
|
euvRGxRGbOZjCPalqQfu4Z7CWywTPdX2q/usyPe7u+D8SzD0ICdhBk0A86qi4J84
|
||||||
p56s9VyZKHG7FGMueXcRsFV8QnESOFZ6BlybRFUFJt6pgFcGUa9QVLHDudn+hsJI
|
/1cWHmtotssN9tNNLGGoGKmDkxawqElQWeHTlNvxzFzW5CI9KyYPD5cxaFtr+jsY
|
||||||
dhiCs5LKxFYoAKEXzJTafMqnnuwRGVrDCbBn6tL9wzBn0MerYD4YEmegglnEtNpX
|
yc2Av6PDnKOLH3HFUo03eoeq9p7ov5NGQswDFxdlTEDcbpaPZDcYiV+Z5zJPMJOs
|
||||||
nbo/+15VXsiU90hoXJGDsVOdCEJRyrANL98NhAci3+2IKQQQY6VgFX0TJqKrJFFc
|
ve2otw9WcximzSs9mryNgLEFSlRTG31oNzLI5E92oHS3pkYRNgW0Wr1SpzetRk+6
|
||||||
qNNV+5VQbcJcceSwZUg/dUCYkTmhvW6Vn9vB7R8AX0r+KT4ByWM=
|
JhCp7f3C2dwUx3/TxeNX/F5j/vQvYrbS0Km83a7CzgEIvfrJEYs=
|
||||||
=R1GH
|
=8zXb
|
||||||
-----END PGP SIGNATURE-----
|
-----END PGP SIGNATURE-----
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
64263feac7b00952e9ec3b6c1fd11316faa58ff673c6bd085fac9f6f8d8389f6 .gitignore
|
64263feac7b00952e9ec3b6c1fd11316faa58ff673c6bd085fac9f6f8d8389f6 .gitignore
|
||||||
3725c292d31a893395e3316046a2ac9264410f62e0a3bf0ae774cff6234d8533 README.md
|
c5649b242a88aec7617c627b75d3d6c6057123fdd9f1ebd8fee1d7001a827da0 README.md
|
||||||
b470a31ed13fb1177a0ffae1872f3e24f9aa8292090f4d838ad2a4bc639f8404 sig
|
bbe6f757d0f17cd0eab5b4cafbd7351e2cd403a7386ac47a2a894115b6f29683 sig
|
||||||
|
|
34
README.md
34
README.md
|
@ -124,3 +124,37 @@ sig verify --threshold 2 --group myteam --method git
|
||||||
```
|
```
|
||||||
sig add
|
sig add
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Frequently Asked Questions
|
||||||
|
|
||||||
|
### Why Bash?
|
||||||
|
|
||||||
|
Because it is easy to quickly verify at any time, has wide OS compatibility andthe majority of the needed operations are calling other programs already on
|
||||||
|
your system like gpg and openssl.
|
||||||
|
|
||||||
|
If this were in another language it would be harder to audit on the fly, would
|
||||||
|
require the user to have a specific language toolchain installed, and it would
|
||||||
|
still mostly just be a bunch of shell executions to call system binaries
|
||||||
|
anyway.
|
||||||
|
|
||||||
|
### Why PGP?
|
||||||
|
|
||||||
|
In spite of many popular claims to the contrary, PGP is still the most well
|
||||||
|
supported protocol for distribution, verification, and signing for keys held
|
||||||
|
by individual humans. It is also the only protocoal with wide HSM support
|
||||||
|
allowing you to keep keys out of system memory and requier physical approval
|
||||||
|
for each operation. E.G a trezor, ledger, or yubikey.
|
||||||
|
|
||||||
|
Popular alternatives like signify or straight openssl have poor support for
|
||||||
|
these workflows.
|
||||||
|
|
||||||
|
Admittedly the GnuPG codebase itself is a buggy dated mess, but PGP as a spec
|
||||||
|
is still Pretty Good for many use cases. A recent modern rewrite by a number
|
||||||
|
of former GnuPG team members is near complete and set to give PGP a long and
|
||||||
|
stable future.
|
||||||
|
|
||||||
|
See: https://sequoia-pgp.org/
|
||||||
|
|
||||||
|
The only promising alternative to GnuPG for software signing that has hsm
|
||||||
|
support and the very attractive feature of expiring signatures is [The Update Framework](https://theupdateframework.io) which may be supported as an alternate
|
||||||
|
method in the future if m-of-n multisig is ever implemented.
|
||||||
|
|
69
sig
69
sig
|
@ -36,7 +36,7 @@ die_pkg() {
|
||||||
*) die "Error: Your operating system is not supported" ;;
|
*) die "Error: Your operating system is not supported" ;;
|
||||||
esac
|
esac
|
||||||
echo "Error: ${package} ${version}+ does not appear to be installed." >&2
|
echo "Error: ${package} ${version}+ does not appear to be installed." >&2
|
||||||
[ ! -z "$install_cmd" ] && printf "Try: \`${install_cmd}\`" >&2
|
[ ! -z "$install_cmd" ] && echo "Try: \`${install_cmd}\`" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,9 +60,8 @@ check_version(){
|
||||||
|
|
||||||
### Check if required binaries are installed at appropriate versions
|
### Check if required binaries are installed at appropriate versions
|
||||||
check_tools(){
|
check_tools(){
|
||||||
if [ -z "${BASH_VERSINFO}" ] \
|
if [ -z "${BASH_VERSINFO[0]}" ] \
|
||||||
|| [ -z "${BASH_VERSINFO[0]}" ] \
|
|| [ "${BASH_VERSINFO[0]}" -lt "${MIN_BASH_VERSION}" ]; then
|
||||||
|| [ ${BASH_VERSINFO[0]} -lt ${MIN_BASH_VERSION} ]; then
|
|
||||||
die_pkg "bash" "${MIN_BASH_VERSION}"
|
die_pkg "bash" "${MIN_BASH_VERSION}"
|
||||||
fi
|
fi
|
||||||
for cmd in "$@"; do
|
for cmd in "$@"; do
|
||||||
|
@ -84,19 +83,6 @@ check_tools(){
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
### Handle different implementations of mktemp across platforms
|
|
||||||
get_temp(){
|
|
||||||
echo "$(
|
|
||||||
mktemp \
|
|
||||||
--quiet \
|
|
||||||
--directory \
|
|
||||||
-t "$(basename "$0").XXXXXX" 2>/dev/null
|
|
||||||
|| mktemp \
|
|
||||||
--quiet \
|
|
||||||
--directory
|
|
||||||
)"
|
|
||||||
}
|
|
||||||
|
|
||||||
### Get files that will be added to the manifest for signing
|
### Get files that will be added to the manifest for signing
|
||||||
### Use git if available, else fall back to find
|
### Use git if available, else fall back to find
|
||||||
get_files(){
|
get_files(){
|
||||||
|
@ -120,6 +106,13 @@ get_signer(){
|
||||||
| head -n1
|
| head -n1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_group_config(){
|
||||||
|
local group=${1?}
|
||||||
|
gpg --with-colons --list-config group \
|
||||||
|
| grep -i "^cfg:group:${group}:" \
|
||||||
|
|| die "Error: group \"${group}\" not found in ~/.gnupg/gpg.conf"
|
||||||
|
}
|
||||||
|
|
||||||
### Verify a file has 0-N unique valid detached signatures
|
### Verify a file has 0-N unique valid detached signatures
|
||||||
### Optionally verify all signatures belong to keys in gpg alias group
|
### Optionally verify all signatures belong to keys in gpg alias group
|
||||||
verify_detached() {
|
verify_detached() {
|
||||||
|
@ -133,13 +126,6 @@ verify_detached() {
|
||||||
local fingerprint
|
local fingerprint
|
||||||
local signer
|
local signer
|
||||||
|
|
||||||
if [ ! -z "$group" ]; then
|
|
||||||
group_config="$( \
|
|
||||||
gpg --with-colons --list-config group \
|
|
||||||
| grep -i "^cfg:group:${group}:" \
|
|
||||||
)" || die "Error: group \"${group}\" not found in ~/.gnupg/gpg.conf"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for sig_filename in "${filename%.*}".*.asc; do
|
for sig_filename in "${filename%.*}".*.asc; do
|
||||||
gpg --verify "${sig_filename}" "${filename}" >/dev/null 2>&1 || {
|
gpg --verify "${sig_filename}" "${filename}" >/dev/null 2>&1 || {
|
||||||
echo "Invalid signature: ${sig_filename}";
|
echo "Invalid signature: ${sig_filename}";
|
||||||
|
@ -155,9 +141,11 @@ verify_detached() {
|
||||||
[[ "${seen_fingerprints}" == *"${fingerprint}"* ]] \
|
[[ "${seen_fingerprints}" == *"${fingerprint}"* ]] \
|
||||||
&& die "Duplicate signature: ${sig_filename}";
|
&& die "Duplicate signature: ${sig_filename}";
|
||||||
|
|
||||||
[ ! -z "$group_config" ] \
|
if [ ! -z "$group" ]; then
|
||||||
&& [[ "${group_config}" != *"${fingerprint}"* ]] \
|
group_config=$(get_group_config "${group}")
|
||||||
|
[[ "${group_config}" != *"${fingerprint}"* ]] \
|
||||||
&& die "Signature not in group \"${group}\": ${sig_filename}";
|
&& die "Signature not in group \"${group}\": ${sig_filename}";
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Verified detached signature by \"${signer}\""
|
echo "Verified detached signature by \"${signer}\""
|
||||||
|
|
||||||
|
@ -175,10 +163,10 @@ verify_git(){
|
||||||
[ $# -eq 2 ] || die "Usage: verify_git <threshold> <group>"
|
[ $# -eq 2 ] || die "Usage: verify_git <threshold> <group>"
|
||||||
local threshold="${1}"
|
local threshold="${1}"
|
||||||
local group="${2}"
|
local group="${2}"
|
||||||
local sig_count=0
|
local group_config=""
|
||||||
local seen_fingerprints=""
|
local seen_fingerprints=""
|
||||||
|
local sig_count=0
|
||||||
local depth=0
|
local depth=0
|
||||||
#TODO: implement group validation
|
|
||||||
|
|
||||||
while [[ $depth != "$(git rev-list --count HEAD)" ]]; do
|
while [[ $depth != "$(git rev-list --count HEAD)" ]]; do
|
||||||
ref=HEAD~${depth}
|
ref=HEAD~${depth}
|
||||||
|
@ -189,6 +177,12 @@ verify_git(){
|
||||||
git verify-commit HEAD~${depth} >/dev/null 2>&1\
|
git verify-commit HEAD~${depth} >/dev/null 2>&1\
|
||||||
|| die "Unsigned commit: ${commit}"
|
|| die "Unsigned commit: ${commit}"
|
||||||
|
|
||||||
|
if [ ! -z "$group" ]; then
|
||||||
|
group_config=$(get_group_config "${group}")
|
||||||
|
[[ "${group_config}" != *"${fingerprint}"* ]] \
|
||||||
|
&& die "Git signing key not in group \"${group}\": ${fingerprint}";
|
||||||
|
fi
|
||||||
|
|
||||||
[[ "${seen_fingerprints}" != *"${fingerprint}"* ]] \
|
[[ "${seen_fingerprints}" != *"${fingerprint}"* ]] \
|
||||||
&& seen_fingerprints="${seen_fingerprints} ${fingerprint}" \
|
&& seen_fingerprints="${seen_fingerprints} ${fingerprint}" \
|
||||||
&& ((sig_count=sig_count+1)) \
|
&& ((sig_count=sig_count+1)) \
|
||||||
|
@ -208,7 +202,7 @@ verify_git(){
|
||||||
|
|
||||||
cmd_manifest() {
|
cmd_manifest() {
|
||||||
mkdir -p ".${PROGRAM}"
|
mkdir -p ".${PROGRAM}"
|
||||||
printf "$(get_files | xargs openssl sha256 -r)" \
|
printf "%s" "$(get_files | xargs openssl sha256 -r)" \
|
||||||
| sed -e 's/ \*/ /g' -e 's/ \.\// /g' \
|
| sed -e 's/ \*/ /g' -e 's/ \.\// /g' \
|
||||||
| LC_ALL=C sort -k2 \
|
| LC_ALL=C sort -k2 \
|
||||||
> ".${PROGRAM}/manifest.txt"
|
> ".${PROGRAM}/manifest.txt"
|
||||||
|
@ -225,7 +219,7 @@ cmd_verify() {
|
||||||
--) shift; break ;;
|
--) shift; break ;;
|
||||||
esac done
|
esac done
|
||||||
|
|
||||||
if ( [ -z "$method" ] || [ "$method" == "git" ] ); then
|
if [ -z "$method" ] || [ "$method" == "git" ]; then
|
||||||
if [ "$method" == "git" ]; then
|
if [ "$method" == "git" ]; then
|
||||||
command -v git >/dev/null 2>&1 \
|
command -v git >/dev/null 2>&1 \
|
||||||
|| die "Error: method 'git' specified and git is not installed"
|
|| die "Error: method 'git' specified and git is not installed"
|
||||||
|
@ -235,23 +229,24 @@ cmd_verify() {
|
||||||
&& verify_git "${threshold}" "${group}"
|
&& verify_git "${threshold}" "${group}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ( [ -z "$method" ] || [ "$method" == "detached" ] ); then
|
if [ -z "$method" ] || [ "$method" == "detached" ]; then
|
||||||
( [ -d ".${PROGRAM}" ] && ls .${PROGRAM}/*.asc >/dev/null 2>&1 ) \
|
( [ -d ".${PROGRAM}" ] && ls ."${PROGRAM}"/*.asc >/dev/null 2>&1 ) \
|
||||||
|| die "Error: No signatures"
|
|| die "Error: No signatures"
|
||||||
cmd_manifest
|
cmd_manifest
|
||||||
verify_detached "${threshold}" "${group}" .${PROGRAM}/manifest.txt
|
verify_detached "${threshold}" "${group}" ."${PROGRAM}"/manifest.txt
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_add(){
|
cmd_add(){
|
||||||
|
local fingerprint
|
||||||
cmd_manifest
|
cmd_manifest
|
||||||
gpg --armor --detach-sig .${PROGRAM}/manifest.txt
|
gpg --armor --detach-sig ."${PROGRAM}"/manifest.txt >/dev/null 2>&1
|
||||||
local fingerprint=$( \
|
fingerprint=$( \
|
||||||
gpg --list-packets .${PROGRAM}/manifest.txt.asc \
|
gpg --list-packets ."${PROGRAM}"/manifest.txt.asc \
|
||||||
| grep "issuer key ID" \
|
| grep "issuer key ID" \
|
||||||
| sed 's/.*\([A-Z0-9]\{16\}\).*/\1/g' \
|
| sed 's/.*\([A-Z0-9]\{16\}\).*/\1/g' \
|
||||||
)
|
)
|
||||||
mv .${PROGRAM}/manifest.{"txt.asc","${fingerprint}.asc"}
|
mv ."${PROGRAM}"/manifest.{"txt.asc","${fingerprint}.asc"}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_version() {
|
cmd_version() {
|
||||||
|
|
Loading…
Reference in New Issue