BROKEN AF, in the middle of a rewrite
This commit is contained in:
parent
eea9cf778e
commit
20388431aa
15
gpg/kant/README
Normal file
15
gpg/kant/README
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
GENERATING THE MAN PAGE:
|
||||||
|
If you have asciidoctor installed, you can generate the manpage one of two ways.
|
||||||
|
|
||||||
|
The first way:
|
||||||
|
|
||||||
|
asciidoctor -b manpage kant.1.adoc -o- | groff -Tascii -man | gz -c > kant.1.gz
|
||||||
|
|
||||||
|
This will generate a fixed-width man page.
|
||||||
|
|
||||||
|
|
||||||
|
The second way (recommended):
|
||||||
|
|
||||||
|
asciidoctor -b manpage kant.1.adoc -o- | gz -c > kant.1.gz
|
||||||
|
|
||||||
|
This will generate a dynamic-width man page. Most modern versions of man want this version.
|
@ -1,14 +1,18 @@
|
|||||||
# NOTE: The python csv module does NOT skip
|
# NOTE: The python csv module does NOT skip
|
||||||
# commented lines!
|
# commented lines!
|
||||||
# This is my personal key. Ultimate trust, push key.
|
# This is my personal key. Ultimate trust,
|
||||||
748231EBCBD808A14F5E85D28C004C2F93481F6B,4,1
|
# push key, careful checking, notify
|
||||||
|
748231EBCBD808A14F5E85D28C004C2F93481F6B,4,1,3,1
|
||||||
# This is a testing junk key generated on a completely separate box,
|
# This is a testing junk key generated on a completely separate box,
|
||||||
# and does not exist on ANY keyservers. Never trust, Never push.
|
# and does not exist on ANY keyservers nor the local keyring.
|
||||||
A03CACFD7123AF443A3A185298A8A46921C8DDEF,-1,-1
|
# Never trust, local sig, unknown checking, don't notify
|
||||||
# This is jthan's key. assign full trust, push to keyserver.
|
A03CACFD7123AF443A3A185298A8A46921C8DDEF,-1,0,0,0
|
||||||
EFD9413B17293AFDFE6EA6F1402A088DEDF104CB,full,true
|
# This is jthan's key.
|
||||||
# This is paden's key. assign Marginal trust, push to keyserver.
|
# assign full trust, push to keyserver, casual checking, notify
|
||||||
6FA8AE12AEC90B035EEE444FE70457341A63E830,2,True
|
EFD9413B17293AFDFE6EA6F1402A088DEDF104CB,full,true,casual,yes
|
||||||
|
# This is paden's key.
|
||||||
|
# assign Marginal trust, push to keyserver, casual checking, notify
|
||||||
|
6FA8AE12AEC90B035EEE444FE70457341A63E830,2,True,Casual,True
|
||||||
# This is the email for the Sysadministrivia serverkey.
|
# This is the email for the Sysadministrivia serverkey.
|
||||||
# Assign full trust, push to keyserver.
|
# Assign full trust, push to keyserver, careful checking, don't notify
|
||||||
<admin@sysadministrivia.com>, full, yes
|
<admin@sysadministrivia.com>, full, yes, careful, false
|
||||||
|
|
225
gpg/kant/kant.1
Normal file
225
gpg/kant/kant.1
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
'\" t
|
||||||
|
.\" Title: kant
|
||||||
|
.\" Author: Brent Saner
|
||||||
|
.\" Generator: Asciidoctor 1.5.5
|
||||||
|
.\" Date: 2017-09-07
|
||||||
|
.\" Manual: KANT - Keysigning and Notification Tool
|
||||||
|
.\" Source: KANT
|
||||||
|
.\" Language: English
|
||||||
|
.\"
|
||||||
|
.TH "KANT" "1" "2017-09-07" "KANT" "KANT \- Keysigning and Notification Tool"
|
||||||
|
.ie \n(.g .ds Aq \(aq
|
||||||
|
.el .ds Aq '
|
||||||
|
.ss \n[.ss] 0
|
||||||
|
.nh
|
||||||
|
.ad l
|
||||||
|
.de URL
|
||||||
|
\\$2 \(laURL: \\$1 \(ra\\$3
|
||||||
|
..
|
||||||
|
.if \n[.g] .mso www.tmac
|
||||||
|
.LINKSTYLE blue R < >
|
||||||
|
.SH "NAME"
|
||||||
|
kant \- Sign GnuPG/OpenPGP/PGP keys and notify the key owner(s)
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.sp
|
||||||
|
\fBkant\fP [\fIOPTION\fP] \-k/\-\-key \fI<KEY_IDS|BATCHFILE>\fP
|
||||||
|
.SH "OPTIONS"
|
||||||
|
.sp
|
||||||
|
Keysigning (and keysigning parties) can be a lot of fun, and can offer someone with new keys a way into the WoT (Web\-of\-Trust).
|
||||||
|
Unfortunately, they can be intimidating to those new to the experience.
|
||||||
|
This tool offers a simple and easy\-to\-use interface to sign public keys (normal, local\-only, and/or non\-exportable),
|
||||||
|
set owner trust, specify level of checking done, and push the signatures to a keyserver. It even supports batch operation via a CSV file.
|
||||||
|
.sp
|
||||||
|
\fB\-h\fP, \fB\-\-help\fP
|
||||||
|
.RS 4
|
||||||
|
Display brief help/usage and exit.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-k\fP \fIKEY_IDS|BATCHFILE\fP, \fB\-\-key\fP \fIKEY_IDS|BATCHFILE\fP
|
||||||
|
.RS 4
|
||||||
|
A single or comma\-separated list of key IDs (see \fBKEY ID FORMAT\fP) to sign, trust, and notify. Can also be an email address.
|
||||||
|
If \fB\-b\fP/\fB\-\-batch\fP is specified, this should instead be a path to the batch file (see \fBBATCHFILE/Format\fP).
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-K\fP \fIKEY_ID\fP, \fB\-\-sigkey\fP \fIKEY_ID\fP
|
||||||
|
.RS 4
|
||||||
|
The key to use when signing other keys (see \fBKEY ID FORMAT\fP). The default key is automatically determined at runtime
|
||||||
|
(it will be displayed in \fB\-h\fP/\fB\-\-help\fP output).
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-t\fP \fITRUSTLEVEL\fP, \fB\-\-trust\fP \fITRUSTLEVEL\fP
|
||||||
|
.RS 4
|
||||||
|
The trust level to automatically apply to all keys (if not specified, KANT will prompt for each key).
|
||||||
|
See \fBBATCHFILE/TRUSTLEVEL\fP for trust level notations.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-c\fP \fICHECKLEVEL\fP, \fB\-\-check\fP \fICHECKLEVEL\fP
|
||||||
|
.RS 4
|
||||||
|
The level of checking that was done to confirm the validity of ownership for all keys being signed. If not specified,
|
||||||
|
the default is for KANT to prompt for each key we sign. See \fBBATCHFILE/CHECKLEVEL\fP for check level notations.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-l\fP \fILOCAL\fP, \fB\-\-local\fP \fILOCAL\fP
|
||||||
|
.RS 4
|
||||||
|
If specified, make the signature(s) local\-only (i.e. non\-exportable, don\(cqt push to a keyserver).
|
||||||
|
See \fBBATCHFILE/LOCAL\fP for more information on local signatures.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-n\fP, \fB\-\-no\-notify\fP
|
||||||
|
.RS 4
|
||||||
|
This requires some explanation. If you have MSMTP[1] installed and configured for the currently active user,
|
||||||
|
then we will send out emails to recipients letting them know we have signed their key. However, if MSMTP is installed and configured
|
||||||
|
but this flag is given, then we will NOT attempt to send emails.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-s\fP \fIKEYSERVER(S)\fP, \fB\-\-keyservers\fP \fIKEYSERVER(S)\fP
|
||||||
|
.RS 4
|
||||||
|
The comma\-separated keyserver(s) to push to. The default keyserver list is automatically generated at runtime.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-b\fP, \fB\-\-batch\fP
|
||||||
|
.RS 4
|
||||||
|
If specified, operate in batch mode. See \fBBATCHFILE\fP for more information.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-D\fP \fIGPGDIR\fP, \fB\-\-gpgdir\fP \fIGPGDIR\fP
|
||||||
|
.RS 4
|
||||||
|
The GnuPG configuration directory to use (containing your keys, etc.). The default is automatically generated at runtime,
|
||||||
|
but will probably be \fB/home/<yourusername>/.gnupg\fP or similar.
|
||||||
|
.RE
|
||||||
|
.sp
|
||||||
|
\fB\-T\fP, \fB\-\-testkeyservers\fP
|
||||||
|
.RS 4
|
||||||
|
If specified, initiate a basic test connection with each set keyserver before anything else. Disabled by default.
|
||||||
|
.RE
|
||||||
|
.SH "KEY ID FORMAT"
|
||||||
|
.sp
|
||||||
|
Key IDs can be specified in one of two ways. The first (and preferred) way is to use the full 160\-bit (40\-character, hexadecimal) key ID.
|
||||||
|
A little known fact is the fingerprint of a key:
|
||||||
|
.sp
|
||||||
|
\fBDEAD BEEF DEAD BEEF DEAD BEEF DEAD BEEF DEAD BEEF\fP
|
||||||
|
.sp
|
||||||
|
is actually the full key ID of the primary key; i.e.:
|
||||||
|
.sp
|
||||||
|
\fBDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF\fP
|
||||||
|
.sp
|
||||||
|
The second way to specify a key, as far as KANT is concerned, is to use an email address.
|
||||||
|
Do note that if more than one key is found that matches the email address given (and they usually are), you will be prompted to select the specific
|
||||||
|
correct key ID anyways so it\(cqs usually a better idea to have the owner present their full key ID/fingerprint right from the get\-go.
|
||||||
|
.SH "BATCHFILE"
|
||||||
|
.SS "Format"
|
||||||
|
.sp
|
||||||
|
The batch file is a CSV\-formatted (comma\-delimited) file containing keys to sign and other information about them. It keeps the following format:
|
||||||
|
.sp
|
||||||
|
\fBKEY_ID,TRUSTLEVEL,LOCAL,CHECKLEVEL,NOTIFY\fP
|
||||||
|
.sp
|
||||||
|
For more information on each column, reference the appropriate sub\-section below.
|
||||||
|
.SS "KEY_ID"
|
||||||
|
.sp
|
||||||
|
See \fBKEY ID FORMAT\fP.
|
||||||
|
.SS "TRUSTLEVEL"
|
||||||
|
.sp
|
||||||
|
The \fITRUSTLEVEL\fP is specified by the following levels (you can use either the numeric or string representation):
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
\fB\-1 = Never
|
||||||
|
0 = Unknown
|
||||||
|
1 = Untrusted
|
||||||
|
2 = Marginal
|
||||||
|
3 = Full
|
||||||
|
4 = Ultimate\fP
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.sp
|
||||||
|
It is how much trust to assign to a key, and the signatures that key makes on other keys.[2]
|
||||||
|
.SS "LOCAL"
|
||||||
|
.sp
|
||||||
|
Whether or not to push to a keyserver. It can be either the numeric or string representation of the following:
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
\fB0 = False
|
||||||
|
1 = True\fP
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.sp
|
||||||
|
If \fB1/True\fP, KANT will sign the key with a local signature (and the signature will not be pushed to a keyserver or be exportable).[3]
|
||||||
|
.SS "CHECKLEVEL"
|
||||||
|
.sp
|
||||||
|
The amount of checking that has been done to confirm that the owner of the key is who they say they are and that the key matches their provided information.
|
||||||
|
It can be either the numeric or string representation of the following:
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
\fB0 = Unknown
|
||||||
|
1 = None
|
||||||
|
2 = Casual
|
||||||
|
3 = Careful\fP
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.sp
|
||||||
|
It is up to you to determine the classification of the amount of checking you have done, but the following is recommended (it is the policy
|
||||||
|
the author follows):
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
\fBUnknown:\fP The key is unknown and has not been reviewed
|
||||||
|
|
||||||
|
\fBNone:\fP The key has been signed, but no confirmation of the
|
||||||
|
ownership of the key has been performed (typically
|
||||||
|
a local signature)
|
||||||
|
|
||||||
|
\fBCasual:\fP The key has been presented and the owner is either
|
||||||
|
known to the signer or they have provided some form
|
||||||
|
of non\-government\-issued identification or other
|
||||||
|
proof (website, Keybase.io, etc.)
|
||||||
|
|
||||||
|
\fBCareful:\fP The same as \fBCasual\fP requirements but they have
|
||||||
|
provided a government\-issued ID and all information
|
||||||
|
matches
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.sp
|
||||||
|
It\(cqs important to check each key you sign carefully. Failure to do so may hurt others\(aq trust in your key.[4]
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.sp
|
||||||
|
gpg(1), gpgconf(1)
|
||||||
|
.SH "RESOURCES"
|
||||||
|
.sp
|
||||||
|
\fBAuthor\(cqs web site:\fP \c
|
||||||
|
.URL "https://square\-r00t.net/" "" ""
|
||||||
|
\fBAuthor\(cqs GPG information:\fP \c
|
||||||
|
.URL "https://square\-r00t.net/gpg\-info" "" ""
|
||||||
|
.SH "COPYING"
|
||||||
|
.sp
|
||||||
|
Copyright (C) 2017 Brent Saner.
|
||||||
|
.sp
|
||||||
|
Free use of this software is granted under the terms of the GPLv3 License.
|
||||||
|
.SH "NOTES"
|
||||||
|
1. http://msmtp.sourceforge.net/
|
||||||
|
2. For more information on trust levels and the Web of Trust, see: https://www.gnupg.org/gph/en/manual/x334.html and https://www.gnupg.org/gph/en/manual/x547.html
|
||||||
|
3. For more information on pushing to keyservers and local signatures, see: https://www.gnupg.org/gph/en/manual/r899.html#LSIGN and https://lists.gnupg.org/pipermail/gnupg-users/2007-January/030242.html
|
||||||
|
4. GnuPG documentation refers to this as "validity"; see https://www.gnupg.org/gph/en/manual/x334.html
|
||||||
|
.SH "AUTHOR(S)"
|
||||||
|
.sp
|
||||||
|
\fBBrent Saner\fP
|
||||||
|
.RS 4
|
||||||
|
Author(s).
|
||||||
|
.RE
|
@ -8,7 +8,7 @@ v1.0.0
|
|||||||
|
|
||||||
== NAME
|
== NAME
|
||||||
|
|
||||||
kant - Sign GnuPG/OpenPGP/PGP keys and notify the key owner(s)
|
KANT - Sign GnuPG/OpenPGP/PGP keys and notify the key owner(s)
|
||||||
|
|
||||||
== SYNOPSIS
|
== SYNOPSIS
|
||||||
|
|
||||||
@ -26,25 +26,28 @@ set owner trust, specify level of checking done, and push the signatures to a ke
|
|||||||
|
|
||||||
*-k* _KEY_IDS|BATCHFILE_, *--key* _KEY_IDS|BATCHFILE_::
|
*-k* _KEY_IDS|BATCHFILE_, *--key* _KEY_IDS|BATCHFILE_::
|
||||||
A single or comma-separated list of key IDs (see *KEY ID FORMAT*) to sign, trust, and notify. Can also be an email address.
|
A single or comma-separated list of key IDs (see *KEY ID FORMAT*) to sign, trust, and notify. Can also be an email address.
|
||||||
If *-b*/*--batch* is specified, this should instead be a path to the batch file (see *BATCHFILE*).
|
If *-b*/*--batch* is specified, this should instead be a path to the batch file (see *BATCHFILE/Format*).
|
||||||
|
|
||||||
*-K* _KEY_ID_, *--sigkey* _KEY_ID_::
|
*-K* _KEY_ID_, *--sigkey* _KEY_ID_::
|
||||||
The key to use when signing other keys (see *KEY ID FORMAT*). The default key is automatically determined at runtime
|
The key to use when signing other keys (see *KEY ID FORMAT*). The default key is automatically determined at runtime
|
||||||
(it will be displayed in *-h*/*--help* output).
|
(it will be displayed in *-h*/*--help* output).
|
||||||
|
|
||||||
*-t* _TRUSTLEVEL_, *--trustlevel* _TRUSTLEVEL_::
|
*-t* _TRUSTLEVEL_, *--trust* _TRUSTLEVEL_::
|
||||||
The trust level to automatically apply to all keys (if not specified, kant will prompt for each key). See *BATCHFILE* for trust level notations.
|
The trust level to automatically apply to all keys (if not specified, KANT will prompt for each key).
|
||||||
|
See *BATCHFILE/TRUSTLEVEL* for trust level notations.
|
||||||
|
|
||||||
*-c* _CHECKLEVEL_, *--checklevel* _CHECKLEVEL_::
|
*-c* _CHECKLEVEL_, *--check* _CHECKLEVEL_::
|
||||||
The level of checking that was done to confirm the validity of ownership for all keys being signed. If not specified,
|
The level of checking that was done to confirm the validity of ownership for all keys being signed. If not specified,
|
||||||
the default is for kant to prompt for each key we sign. See *BATCHFILE* for check level notations.
|
the default is for KANT to prompt for each key we sign. See *BATCHFILE/CHECKLEVEL* for check level notations.
|
||||||
|
|
||||||
*-e* _EXPORT_, *--export* _EXPORT_::
|
|
||||||
Whether the signature(s) should be made exportable or not. See *BATCHFILE* for more information on exportability.
|
|
||||||
The default is True (signatures will be exportable).
|
|
||||||
|
|
||||||
*-l* _LOCAL_, *--local* _LOCAL_::
|
*-l* _LOCAL_, *--local* _LOCAL_::
|
||||||
Make the signature(s) local-only (i.e. don't push to a keyserver).
|
If specified, make the signature(s) local-only (i.e. non-exportable, don't push to a keyserver).
|
||||||
|
See *BATCHFILE/LOCAL* for more information on local signatures.
|
||||||
|
|
||||||
|
*-n*, *--no-notify*::
|
||||||
|
This requires some explanation. If you have MSMTPfootnote:[\http://msmtp.sourceforge.net/] installed and configured for the currently active user,
|
||||||
|
then we will send out emails to recipients letting them know we have signed their key. However, if MSMTP is installed and configured
|
||||||
|
but this flag is given, then we will NOT attempt to send emails.
|
||||||
|
|
||||||
*-s* _KEYSERVER(S)_, *--keyservers* _KEYSERVER(S)_::
|
*-s* _KEYSERVER(S)_, *--keyservers* _KEYSERVER(S)_::
|
||||||
The comma-separated keyserver(s) to push to. The default keyserver list is automatically generated at runtime.
|
The comma-separated keyserver(s) to push to. The default keyserver list is automatically generated at runtime.
|
||||||
@ -52,7 +55,7 @@ set owner trust, specify level of checking done, and push the signatures to a ke
|
|||||||
*-b*, *--batch*::
|
*-b*, *--batch*::
|
||||||
If specified, operate in batch mode. See *BATCHFILE* for more information.
|
If specified, operate in batch mode. See *BATCHFILE* for more information.
|
||||||
|
|
||||||
*-d* _GPGDIR_, *--gpgdir* _GPGDIR_::
|
*-D* _GPGDIR_, *--gpgdir* _GPGDIR_::
|
||||||
The GnuPG configuration directory to use (containing your keys, etc.). The default is automatically generated at runtime,
|
The GnuPG configuration directory to use (containing your keys, etc.). The default is automatically generated at runtime,
|
||||||
but will probably be */home/<yourusername>/.gnupg* or similar.
|
but will probably be */home/<yourusername>/.gnupg* or similar.
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ is actually the full key ID of the primary key; i.e.:
|
|||||||
*DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF*
|
*DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF*
|
||||||
|
|
||||||
The second way to specify a key, as far as KANT is concerned, is to use an email address.
|
The second way to specify a key, as far as KANT is concerned, is to use an email address.
|
||||||
Do note that if more than one key is found that matches the email address given, you will be prompted to select the specific
|
Do note that if more than one key is found that matches the email address given (and they usually are), you will be prompted to select the specific
|
||||||
correct key ID anyways so it's usually a better idea to have the owner present their full key ID/fingerprint right from the get-go.
|
correct key ID anyways so it's usually a better idea to have the owner present their full key ID/fingerprint right from the get-go.
|
||||||
|
|
||||||
== BATCHFILE
|
== BATCHFILE
|
||||||
@ -78,22 +81,85 @@ correct key ID anyways so it's usually a better idea to have the owner present t
|
|||||||
=== Format
|
=== Format
|
||||||
The batch file is a CSV-formatted (comma-delimited) file containing keys to sign and other information about them. It keeps the following format:
|
The batch file is a CSV-formatted (comma-delimited) file containing keys to sign and other information about them. It keeps the following format:
|
||||||
|
|
||||||
*KEY_ID,TRUSTLEVEL,PUSH,CHECKLEVEL,EXPORT*
|
*KEY_ID,TRUSTLEVEL,LOCAL,CHECKLEVEL,NOTIFY*
|
||||||
|
|
||||||
|
For more information on each column, reference the appropriate sub-section below.
|
||||||
|
|
||||||
=== KEY_ID
|
=== KEY_ID
|
||||||
See *KEY ID FORMAT*.
|
See *KEY ID FORMAT*.
|
||||||
|
|
||||||
=== TRUSTLEVEL
|
=== TRUSTLEVEL
|
||||||
The _TRUSTLEVEL_ is specified by the following levels:
|
The _TRUSTLEVEL_ is specified by the following levels (you can use either the numeric or string representation):
|
||||||
|
|
||||||
*THIS IS A TEST*
|
[subs=+quotes]
|
||||||
|
....
|
||||||
|
*-1 = Never
|
||||||
|
0 = Unknown
|
||||||
|
1 = Untrusted
|
||||||
|
2 = Marginal
|
||||||
|
3 = Full
|
||||||
|
4 = Ultimate*
|
||||||
|
....
|
||||||
|
|
||||||
|
It is how much trust to assign to a key, and the signatures that key makes on other keys.footnote:[For more information
|
||||||
|
on trust levels and the Web of Trust, see: \https://www.gnupg.org/gph/en/manual/x334.html and \https://www.gnupg.org/gph/en/manual/x547.html]
|
||||||
|
|
||||||
|
=== LOCAL
|
||||||
|
Whether or not to push to a keyserver. It can be either the numeric or string representation of the following:
|
||||||
|
|
||||||
|
[subs=+quotes]
|
||||||
|
....
|
||||||
|
*0 = False
|
||||||
|
1 = True*
|
||||||
|
....
|
||||||
|
|
||||||
|
If *1/True*, KANT will sign the key with a local signature (and the signature will not be pushed to a keyserver or be exportable).footnote:[For
|
||||||
|
more information on pushing to keyservers and local signatures, see: \https://www.gnupg.org/gph/en/manual/r899.html#LSIGN and
|
||||||
|
\https://lists.gnupg.org/pipermail/gnupg-users/2007-January/030242.html]
|
||||||
|
|
||||||
|
=== CHECKLEVEL
|
||||||
|
The amount of checking that has been done to confirm that the owner of the key is who they say they are and that the key matches their provided information.
|
||||||
|
It can be either the numeric or string representation of the following:
|
||||||
|
|
||||||
|
[subs=+quotes]
|
||||||
|
....
|
||||||
|
*0 = Unknown
|
||||||
|
1 = None
|
||||||
|
2 = Casual
|
||||||
|
3 = Careful*
|
||||||
|
....
|
||||||
|
|
||||||
|
It is up to you to determine the classification of the amount of checking you have done, but the following is recommended (it is the policy
|
||||||
|
the author follows):
|
||||||
|
|
||||||
|
[subs=+quotes]
|
||||||
|
....
|
||||||
|
*Unknown:* The key is unknown and has not been reviewed
|
||||||
|
|
||||||
|
*None:* The key has been signed, but no confirmation of the
|
||||||
|
ownership of the key has been performed (typically
|
||||||
|
a local signature)
|
||||||
|
|
||||||
|
*Casual:* The key has been presented and the owner is either
|
||||||
|
known to the signer or they have provided some form
|
||||||
|
of non-government-issued identification or other
|
||||||
|
proof (website, Keybase.io, etc.)
|
||||||
|
|
||||||
|
*Careful:* The same as *Casual* requirements but they have
|
||||||
|
provided a government-issued ID and all information
|
||||||
|
matches
|
||||||
|
....
|
||||||
|
|
||||||
|
It's important to check each key you sign carefully. Failure to do so may hurt others' trust in your key.footnote:[GnuPG documentation refers
|
||||||
|
to this as "validity"; see \https://www.gnupg.org/gph/en/manual/x334.html]
|
||||||
|
|
||||||
== SEE ALSO
|
== SEE ALSO
|
||||||
gpg(1), gpgcong(1)
|
gpg(1), gpgconf(1)
|
||||||
|
|
||||||
== RESOURCES
|
== RESOURCES
|
||||||
|
|
||||||
*Author's web site:* https://square-r00t.net/
|
*Author's web site:* https://square-r00t.net/
|
||||||
|
*Author's GPG information:* https://square-r00t.net/gpg-info
|
||||||
|
|
||||||
== COPYING
|
== COPYING
|
||||||
|
|
||||||
|
277
gpg/kant/kant.py
277
gpg/kant/kant.py
@ -11,7 +11,9 @@ import subprocess
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from socket import *
|
from socket import *
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import gpgme # non-stdlib; Arch package is "python-pygpgme"
|
import gpg # non-stdlib; Arch package is "python-gpgme" - see
|
||||||
|
# https://git.archlinux.org/svntogit/packages.git/tree/trunk/PKGBUILD?h=packages/gpgme and
|
||||||
|
# https://pypi.python.org/pypi/gpg
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# - http://tanguy.ortolo.eu/blog/article9/pgp-signature-infos edit certification level- possible with pygpgme?
|
# - http://tanguy.ortolo.eu/blog/article9/pgp-signature-infos edit certification level- possible with pygpgme?
|
||||||
@ -36,17 +38,21 @@ import gpgme # non-stdlib; Arch package is "python-pygpgme"
|
|||||||
#
|
#
|
||||||
#Thanks again!
|
#Thanks again!
|
||||||
|
|
||||||
def getKeys(args):
|
class sigsession(object):
|
||||||
|
def __init__(self, args):
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def getKeys(self):
|
||||||
# Get our concept
|
# Get our concept
|
||||||
os.environ['GNUPGHOME'] = args['gpgdir']
|
os.environ['GNUPGHOME'] = self.args['gpgdir']
|
||||||
gpg = gpgme.Context()
|
ctx = gpg.Context()
|
||||||
keys = {}
|
keys = {}
|
||||||
allkeys = []
|
self.keyids = []
|
||||||
# Do we have the key already? If not, fetch.
|
# Do we have the key already? If not, fetch.
|
||||||
for k in args['rcpts'].keys():
|
for k in list(self.args['rcpts']):
|
||||||
if args['rcpts'][k]['type'] == 'fpr':
|
if self.args['rcpts'][k]['type'] == 'fpr':
|
||||||
allkeys.append(k)
|
self.keyids.append(k)
|
||||||
if args['rcpts'][k]['type'] == 'email':
|
if self.args['rcpts'][k]['type'] == 'email':
|
||||||
# We need to actually do a lookup on the email address.
|
# We need to actually do a lookup on the email address.
|
||||||
with open(os.devnull, 'w') as f:
|
with open(os.devnull, 'w') as f:
|
||||||
# TODO: replace with gpg.keylist_mode(gpgme.KEYLIST_MODE_EXTERN) and internal mechanisms?
|
# TODO: replace with gpg.keylist_mode(gpgme.KEYLIST_MODE_EXTERN) and internal mechanisms?
|
||||||
@ -86,12 +92,12 @@ def getKeys(args):
|
|||||||
if key not in keys.keys():
|
if key not in keys.keys():
|
||||||
print('Please enter a full key ID from the list above or hit ctrl-d to exit.')
|
print('Please enter a full key ID from the list above or hit ctrl-d to exit.')
|
||||||
else:
|
else:
|
||||||
allkeys.append(key)
|
self.keyids.append(key)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if not len(keys.keys()) >= 1:
|
if not len(keys.keys()) >= 1:
|
||||||
print('Could not find {0}!'.format(k))
|
print('Could not find {0}!'.format(k))
|
||||||
del(args['rcpts'][k])
|
del(self.args['rcpts'][k])
|
||||||
continue
|
continue
|
||||||
key = list(keys.keys())[0]
|
key = list(keys.keys())[0]
|
||||||
print('\nFound key {0} for {1} (Generated at {2}):'.format(key, k, datetime.datetime.utcfromtimestamp(keys[key]['time'])))
|
print('\nFound key {0} for {1} (Generated at {2}):'.format(key, k, datetime.datetime.utcfromtimestamp(keys[key]['time'])))
|
||||||
@ -99,17 +105,17 @@ def getKeys(args):
|
|||||||
print('\t(Generated {2}) {0} <{1}>'.format(keys[key]['uids'][email]['comment'],
|
print('\t(Generated {2}) {0} <{1}>'.format(keys[key]['uids'][email]['comment'],
|
||||||
email,
|
email,
|
||||||
datetime.datetime.utcfromtimestamp(keys[key]['uids'][email]['time'])))
|
datetime.datetime.utcfromtimestamp(keys[key]['uids'][email]['time'])))
|
||||||
allkeys.append(key)
|
self.keyids.append(key)
|
||||||
print()
|
print()
|
||||||
## And now we can (FINALLY) fetch the key(s).
|
## And now we can (FINALLY) fetch the key(s).
|
||||||
# TODO: replace with gpg.keylist_mode(gpgme.KEYLIST_MODE_EXTERN) and internal mechanisms?
|
# TODO: replace with gpg.keylist_mode(gpgme.KEYLIST_MODE_EXTERN) and internal mechanisms?
|
||||||
recvcmd = ['gpg2', '--recv-keys', '--batch', '--yes'] # We'll add the keys onto the end of this next.
|
recvcmd = ['gpg2', '--recv-keys', '--batch', '--yes'] # We'll add the keys onto the end of this next.
|
||||||
recvcmd.extend(allkeys)
|
recvcmd.extend(self.keyids)
|
||||||
with open(os.devnull, 'w') as f:
|
with open(os.devnull, 'w') as f:
|
||||||
fetchout = subprocess.run(recvcmd, stdout = f, stderr = f) # We hide stderr because gpg, for some unknown reason, spits non-errors to stderr.
|
fetchout = subprocess.run(recvcmd, stdout = f, stderr = f) # We hide stderr because gpg, for some unknown reason, spits non-errors to stderr.
|
||||||
return(allkeys)
|
return(self.keyids)
|
||||||
|
|
||||||
def trustKeys(keyids, args):
|
def trustKeys(self):
|
||||||
# Map the trust levels to "human" equivalent
|
# Map the trust levels to "human" equivalent
|
||||||
trustmap = {-1: ['never', gpgme.VALIDITY_NEVER], # this is... probably? not ideal, but.
|
trustmap = {-1: ['never', gpgme.VALIDITY_NEVER], # this is... probably? not ideal, but.
|
||||||
0: ['unknown', gpgme.VALIDITY_UNKNOWN],
|
0: ['unknown', gpgme.VALIDITY_UNKNOWN],
|
||||||
@ -117,8 +123,7 @@ def trustKeys(keyids, args):
|
|||||||
2: ['marginal', gpgme.VALIDITY_MARGINAL],
|
2: ['marginal', gpgme.VALIDITY_MARGINAL],
|
||||||
3: ['full', gpgme.VALIDITY_FULL],
|
3: ['full', gpgme.VALIDITY_FULL],
|
||||||
4: ['ultimate', gpgme.VALIDITY_ULTIMATE]}
|
4: ['ultimate', gpgme.VALIDITY_ULTIMATE]}
|
||||||
pushmap = {-1: ['never', None],
|
locmap = {0: ['no', False],
|
||||||
0: ['no', False],
|
|
||||||
1: ['yes', True]}
|
1: ['yes', True]}
|
||||||
def promptTrust(kinfo):
|
def promptTrust(kinfo):
|
||||||
for k in list(kinfo):
|
for k in list(kinfo):
|
||||||
@ -131,26 +136,24 @@ def trustKeys(keyids, args):
|
|||||||
if trust_in.lower().strip() == dictv[0]:
|
if trust_in.lower().strip() == dictv[0]:
|
||||||
trust_lvl = int(dictk)
|
trust_lvl = int(dictk)
|
||||||
elif trust_in == str(dictk):
|
elif trust_in == str(dictk):
|
||||||
trust_lvl = dictk
|
trust_lvl = int(dictk)
|
||||||
if not trust_lvl:
|
if not trust_lvl:
|
||||||
print('Not a valid trust level; skipping. Run kant again to fix.')
|
print('Not a valid trust level; skipping. Run kant again to fix.')
|
||||||
continue
|
continue
|
||||||
kinfo[k]['trust'] = trustmap[trust_lvl][1]
|
kinfo[k]['trust'] = trustmap[trust_lvl][1]
|
||||||
if 'push' not in kinfo[k].keys():
|
if 'local' not in kinfo[k].keys():
|
||||||
push = True
|
local = False
|
||||||
if args['keyservers']:
|
if args['keyservers']:
|
||||||
push_in = input('\nShould we push {0} to the keyserver(s) (\033[1mYES\033[0m/No/Never)? '.format(k))
|
local_in = input('\nShould we push {0} to the keyserver(s) (\033[1mYES\033[0m/No)? '.format(k))
|
||||||
if push_in.lower() == 'never':
|
if local_in.lower().startswith('n'):
|
||||||
push = None
|
local = True
|
||||||
elif push_in.lower().startswith('n'):
|
kinfo[k]['local'] = local
|
||||||
push = False
|
|
||||||
kinfo[k]['push'] = push
|
|
||||||
return(kinfo)
|
return(kinfo)
|
||||||
os.environ['GNUPGHOME'] = args['gpgdir']
|
os.environ['GNUPGHOME'] = args['gpgdir']
|
||||||
gpg = gpgme.Context()
|
gpg = gpgme.Context()
|
||||||
# Build out some info about keys
|
# Build out some info about keys
|
||||||
kinfo = {}
|
kinfo = {}
|
||||||
for k in keyids:
|
for k in self.keyids:
|
||||||
if k not in kinfo.keys():
|
if k not in kinfo.keys():
|
||||||
kinfo[k] = {}
|
kinfo[k] = {}
|
||||||
else:
|
else:
|
||||||
@ -163,13 +166,35 @@ def trustKeys(keyids, args):
|
|||||||
print('Can\'t get information about key {0}; skipping.'.format(k))
|
print('Can\'t get information about key {0}; skipping.'.format(k))
|
||||||
del(kinfo[k])
|
del(kinfo[k])
|
||||||
if not args['batch']:
|
if not args['batch']:
|
||||||
trusts = promptTrust(kinfo)
|
if not args['trustlevel']:
|
||||||
|
self.trusts = promptTrust(kinfo)
|
||||||
else:
|
else:
|
||||||
trusts = {}
|
for k in list(kinfo):
|
||||||
|
local = False
|
||||||
|
if 'trust' not in kinfo[k].keys():
|
||||||
|
for dictk, dictv in trustmap.items():
|
||||||
|
if args['trustlevel'].lower().strip() == dictv[0]:
|
||||||
|
trust_lvl = int(dictk)
|
||||||
|
elif args['trustlevel'] == str(dictk):
|
||||||
|
trust_lvl = int(dictk)
|
||||||
|
if not trust_lvl:
|
||||||
|
print('Not a valid trust level; skipping. Run kant again to fix.')
|
||||||
|
continue
|
||||||
|
if 'local' not in kinfo[k].keys():
|
||||||
|
if args['local']:
|
||||||
|
local = True
|
||||||
|
kinfo[k]['local'] = local
|
||||||
|
kinfo[k]['trust'] = trustmap[trust_lvl][1]
|
||||||
|
self.trusts = kinfo
|
||||||
|
else:
|
||||||
|
self.trusts = {}
|
||||||
csvd = {} # We import the CSV into a totally separate dict so we can do some validation loops
|
csvd = {} # We import the CSV into a totally separate dict so we can do some validation loops
|
||||||
with open(args['keys'], 'r') as f:
|
with open(self.args['keys'], 'r') as f:
|
||||||
for row in csv.reader(f, delimiter = ',', quotechar = '"'):
|
for row in csv.reader(f, delimiter = ',', quotechar = '"'):
|
||||||
csvd[row[0]] = {'trust': row[1], 'push': row[2]}
|
csvd[row[0]] = {'trust': row[1].strip(),
|
||||||
|
'local': row[2].strip(),
|
||||||
|
'check': row[3].strip(),
|
||||||
|
'notify': row[4].strip()}
|
||||||
for k in list(csvd):
|
for k in list(csvd):
|
||||||
if re.match('^<?[\w\.\+\-]+\@[\w-]+\.[a-z]{2,3}>?$', k): # is it an email address?
|
if re.match('^<?[\w\.\+\-]+\@[\w-]+\.[a-z]{2,3}>?$', k): # is it an email address?
|
||||||
fullkey = gpg.get_key(k)
|
fullkey = gpg.get_key(k)
|
||||||
@ -177,44 +202,42 @@ def trustKeys(keyids, args):
|
|||||||
del(csvd[k])
|
del(csvd[k])
|
||||||
k = fullkey.subkeys[0].fpr
|
k = fullkey.subkeys[0].fpr
|
||||||
if k not in trusts.keys():
|
if k not in trusts.keys():
|
||||||
trusts[k] = {}
|
self.trusts[k] = {}
|
||||||
if 'trust' not in trusts[k].keys():
|
if 'trust' not in trusts[k].keys():
|
||||||
# Properly index the trust
|
# Properly index the trust
|
||||||
strval = str(csvd[k]['trust']).lower().strip()
|
strval = str(csvd[k]['trust']).lower().strip()
|
||||||
if strval == 'true':
|
if strval == 'true':
|
||||||
trusts[k]['trust'] = True
|
self.trusts[k]['trust'] = True
|
||||||
elif strval == 'false':
|
elif strval == 'false':
|
||||||
trusts[k]['trust'] = False
|
self.trusts[k]['trust'] = False
|
||||||
elif strval == 'none':
|
elif strval == 'none':
|
||||||
trusts[k]['trust'] = None
|
self.trusts[k]['trust'] = None
|
||||||
else:
|
else:
|
||||||
for dictk, dictv in trustmap.items(): # "no"/"yes"
|
for dictk, dictv in trustmap.items():
|
||||||
if strval == dictv[0]:
|
if strval == dictv[0]:
|
||||||
trusts[k]['trust'] = trustmap[dictk][1]
|
self.trusts[k]['trust'] = trustmap[dictk][1]
|
||||||
elif strval == str(dictk):
|
elif strval == str(dictk):
|
||||||
trusts[k]['trust'] = trustmap[dictk][1]
|
self.trusts[k]['trust'] = trustmap[dictk][1]
|
||||||
if 'trust' not in trusts[k].keys(): # yes, again. we make sure it was set. otherwise, we need to skip this key.
|
if 'trust' not in self.trusts[k].keys(): # yes, again. we make sure it was set. otherwise, we need to skip this key.
|
||||||
print('Key {0}: trust level "{1}" is invalid; skipping.'.format(k, csvd[k]['trust']))
|
print('Key {0}: trust level "{1}" is invalid; skipping.'.format(k, csvd[k]['trust']))
|
||||||
del(trusts[k])
|
del(self.trusts[k])
|
||||||
continue
|
continue
|
||||||
# Now we need to index whether we push or not.
|
# Now we need to index whether we push or not.
|
||||||
if 'push' not in trusts[k].keys():
|
if 'local' not in self.trusts[k].keys():
|
||||||
strval = str(csvd[k]['push']).lower().strip()
|
strval = str(csvd[k]['local']).lower().strip()
|
||||||
if strval == 'true':
|
if strval == 'true':
|
||||||
trusts[k]['push'] = True
|
self.trusts[k]['local'] = True
|
||||||
elif strval == 'false':
|
elif strval == 'false':
|
||||||
trusts[k]['push'] = False
|
self.trusts[k]['local'] = False
|
||||||
elif strval == 'none':
|
|
||||||
trusts[k]['push'] = None
|
|
||||||
else:
|
else:
|
||||||
for dictk, dictv in pushmap.items(): # "no"/"yes"
|
for dictk, dictv in locmap.items():
|
||||||
if strval in dictv[0]:
|
if strval in dictv[0]:
|
||||||
trusts[k]['push'] = pushmap[dictk][1]
|
self.trusts[k]['local'] = locmap[dictk][1]
|
||||||
elif strval == str(dictk):
|
elif strval == str(dictk):
|
||||||
trusts[k]['push'] = pushmap[dictk][1]
|
self.trusts[k]['local'] = locmap[dictk][1]
|
||||||
if 'push' not in trusts[k].keys(): # yep. double-check
|
if 'local' not in self.trusts[k].keys(): # yep. double-check
|
||||||
print('Key {0}: push option "{1}" is invalid; skipping.'.format(k, csvd[k]['push']))
|
print('Key {0}: local option "{1}" is invalid; skipping.'.format(k, csvd[k]['local']))
|
||||||
del(trusts[k])
|
del(self.trusts[k])
|
||||||
continue
|
continue
|
||||||
# WHEW. THAT'S A LOT OF VALIDATIONS. Now the Business-End(TM)
|
# WHEW. THAT'S A LOT OF VALIDATIONS. Now the Business-End(TM)
|
||||||
# Reverse mapping of constants to human-readable
|
# Reverse mapping of constants to human-readable
|
||||||
@ -225,18 +248,18 @@ def trustKeys(keyids, args):
|
|||||||
gpgme.VALIDITY_FULL: 'Full',
|
gpgme.VALIDITY_FULL: 'Full',
|
||||||
gpgme.VALIDITY_ULTIMATE: 'Ultimate'}
|
gpgme.VALIDITY_ULTIMATE: 'Ultimate'}
|
||||||
mykey = gpg.get_key(args['sigkey'])
|
mykey = gpg.get_key(args['sigkey'])
|
||||||
for k in list(trusts):
|
for k in list(self.trusts):
|
||||||
keystat = None
|
keystat = None
|
||||||
try:
|
try:
|
||||||
tkey = gpg.get_key(k)
|
tkey = gpg.get_key(k)
|
||||||
except gpgme.GpgmeError:
|
except gpgme.GpgmeError:
|
||||||
print('Cannot find {0} in keyring at all; skipping.'.format(k))
|
print('Cannot find {0} in keyring at all; skipping.'.format(k))
|
||||||
del(trusts[k])
|
del(self.trusts[k])
|
||||||
continue
|
continue
|
||||||
curtrust = rmap[tkey.owner_trust]
|
curtrust = rmap[tkey.owner_trust]
|
||||||
newtrust = rmap[trusts[k]['trust']]
|
newtrust = rmap[self.trusts[k]['trust']]
|
||||||
if tkey.owner_trust == trusts[k]['trust']:
|
if tkey.owner_trust == trusts[k]['trust']:
|
||||||
trusts[k]['change'] = False
|
self.trusts[k]['change'] = False
|
||||||
continue # Don't bother; we aren't changing the trust level, it's the same (OR we haven't trusted yet)
|
continue # Don't bother; we aren't changing the trust level, it's the same (OR we haven't trusted yet)
|
||||||
elif tkey.owner_trust == gpgme.VALIDITY_UNKNOWN:
|
elif tkey.owner_trust == gpgme.VALIDITY_UNKNOWN:
|
||||||
keystat = 'a NEW TRUST'
|
keystat = 'a NEW TRUST'
|
||||||
@ -244,7 +267,7 @@ def trustKeys(keyids, args):
|
|||||||
keystat = 'a DOWNGRADE'
|
keystat = 'a DOWNGRADE'
|
||||||
elif tkey.owner_trust < trusts[k]['trust']:
|
elif tkey.owner_trust < trusts[k]['trust']:
|
||||||
keystat = 'an UPGRADE'
|
keystat = 'an UPGRADE'
|
||||||
print(('\nKey 0x{0} [{1} ({2})]:\n' +
|
print(('\nKey {0} [{1} ({2})]:\n' +
|
||||||
'\tThis trust level ({3}) is {4} from the current trust level ({5}).').format(k,
|
'\tThis trust level ({3}) is {4} from the current trust level ({5}).').format(k,
|
||||||
kinfo[k]['name'],
|
kinfo[k]['name'],
|
||||||
kinfo[k]['email'],
|
kinfo[k]['email'],
|
||||||
@ -253,42 +276,37 @@ def trustKeys(keyids, args):
|
|||||||
curtrust))
|
curtrust))
|
||||||
tchk = input('Continue? (yes/\033[1mNO\033[0m) ')
|
tchk = input('Continue? (yes/\033[1mNO\033[0m) ')
|
||||||
if tchk.lower().startswith('y'):
|
if tchk.lower().startswith('y'):
|
||||||
trusts[k]['change'] = True
|
self.trusts[k]['change'] = True
|
||||||
else:
|
else:
|
||||||
trusts[k]['change'] = False
|
self.trusts[k]['change'] = False
|
||||||
for k in list(trusts):
|
for k in list(self.trusts):
|
||||||
if trusts[k]['change']:
|
if self.trusts[k]['change']:
|
||||||
gpgme.editutil.edit_trust(gpg, k, trusts['k']['trust'])
|
print(k)
|
||||||
|
gpg.editutil.edit_trust(ctx, ctx.get_key(k), self.trusts[k]['trust'])
|
||||||
print()
|
print()
|
||||||
return(trusts)
|
return(self.trusts)
|
||||||
|
|
||||||
def sigKeys(trusts, args): # The More Business-End(TM)
|
def sigKeys(self): # The More Business-End(TM)
|
||||||
import pprint
|
|
||||||
pprint.pprint(trusts)
|
|
||||||
os.environ['GNUPGHOME'] = args['gpgdir']
|
os.environ['GNUPGHOME'] = args['gpgdir']
|
||||||
gpg = gpgme.Context()
|
ctx = gpg.Context()
|
||||||
gpg.keylist_mode = gpgme.KEYLIST_MODE_SIGS
|
ctx.keylist_mode = gpg.KEYLIST_MODE_SIGS
|
||||||
mkey = gpg.get_key(args['sigkey'])
|
mkey = ctx.get_key(args['sigkey'])
|
||||||
gpg.signers = [mkey]
|
ctx.signers = [mkey]
|
||||||
global_policy = {}
|
global_policy = {}
|
||||||
global_policy['push'] = True # I may be able to provide a way to explicitly change this at runtime later
|
for k in list(self.trusts):
|
||||||
global_policy['sign'] = True
|
|
||||||
if not args['keyservers']:
|
|
||||||
global_policy['sign'] = 'local'
|
|
||||||
global_policy['push'] = False
|
|
||||||
for k in list(trusts):
|
|
||||||
sign = True
|
sign = True
|
||||||
key = gpg.get_key(k)
|
key = ctx.get_key(k)
|
||||||
for uid in key.uids:
|
for uid in key.uids:
|
||||||
for s in uid.signatures:
|
for s in uid.signatures:
|
||||||
try:
|
try:
|
||||||
signerkey = gpg.get_key(s.keyid).subkeys[0].fpr
|
signerkey = ctx.get_key(s.keyid).subkeys[0].fpr
|
||||||
if signerkey == mkey.subkeys[0].fpr:
|
if signerkey == mkey.subkeys[0].fpr:
|
||||||
sign = False # We already signed this key
|
sign = False # We already signed this key
|
||||||
except gpgme.GpgmeError:
|
except gpgme.GpgError:
|
||||||
pass # usually if we get this it means we don't have a signer's key in our keyring
|
pass # usually if we get this it means we don't have a signer's key in our keyring
|
||||||
trusts[k]['sign'] = sign
|
self.trusts[k]['sign'] = sign
|
||||||
|
import pprint
|
||||||
|
pprint.pprint(self.trusts)
|
||||||
# edit_sign(ctx, key, index=0, local=False, norevoke=False, expire=True, check=0)
|
# edit_sign(ctx, key, index=0, local=False, norevoke=False, expire=True, check=0)
|
||||||
# index: the index of the user ID to sign, starting at 1. Sign all
|
# index: the index of the user ID to sign, starting at 1. Sign all
|
||||||
# user IDs if set to 0.
|
# user IDs if set to 0.
|
||||||
@ -308,11 +326,11 @@ def sigKeys(trusts, args): # The More Business-End(TM)
|
|||||||
def pushKeys(): # The Last Business-End(TM)
|
def pushKeys(): # The Last Business-End(TM)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def modifyDirmngr(op, args):
|
def modifyDirmngr(self, op):
|
||||||
if not args['keyservers']:
|
if not self.args['keyservers']:
|
||||||
return()
|
return()
|
||||||
pid = str(os.getpid())
|
pid = str(os.getpid())
|
||||||
activecfg = os.path.join(args['gpgdir'], 'dirmngr.conf')
|
activecfg = os.path.join(self.args['gpgdir'], 'dirmngr.conf')
|
||||||
bakcfg = '{0}.{1}'.format(activecfg, pid)
|
bakcfg = '{0}.{1}'.format(activecfg, pid)
|
||||||
if op in ('new', 'start'):
|
if op in ('new', 'start'):
|
||||||
if os.path.lexists(activecfg):
|
if os.path.lexists(activecfg):
|
||||||
@ -322,7 +340,7 @@ def modifyDirmngr(op, args):
|
|||||||
if not line.startswith('keyserver '):
|
if not line.startswith('keyserver '):
|
||||||
write.write(line)
|
write.write(line)
|
||||||
with open(activecfg, 'a') as f:
|
with open(activecfg, 'a') as f:
|
||||||
for s in args['keyservers']:
|
for s in self.args['keyservers']:
|
||||||
uri = '{0}://{1}:{2}'.format(s['proto'], s['server'], s['port'][0])
|
uri = '{0}://{1}:{2}'.format(s['proto'], s['server'], s['port'][0])
|
||||||
f.write('keyserver {0}\n'.format(uri))
|
f.write('keyserver {0}\n'.format(uri))
|
||||||
if op in ('old', 'stop'):
|
if op in ('old', 'stop'):
|
||||||
@ -385,8 +403,8 @@ def parseArgs():
|
|||||||
if not defgpgdir:
|
if not defgpgdir:
|
||||||
return(None)
|
return(None)
|
||||||
defkey = None
|
defkey = None
|
||||||
gpg = gpgme.Context()
|
ctx = gpg.Context()
|
||||||
for k in gpg.keylist(None, True): # params are query and secret keyring, respectively
|
for k in ctx.keylist(None, True): # params are query and secret keyring, respectively
|
||||||
if k.can_sign and True not in (k.revoked, k.expired, k.disabled):
|
if k.can_sign and True not in (k.revoked, k.expired, k.disabled):
|
||||||
defkey = k.subkeys[0].fpr
|
defkey = k.subkeys[0].fpr
|
||||||
break # We'll just use the first primary key we find that's valid as the default.
|
break # We'll just use the first primary key we find that's valid as the default.
|
||||||
@ -410,71 +428,61 @@ def parseArgs():
|
|||||||
defkey = getDefKey(defgpgdir)
|
defkey = getDefKey(defgpgdir)
|
||||||
defkeyservers = getDefKeyservers(defgpgdir)
|
defkeyservers = getDefKeyservers(defgpgdir)
|
||||||
args = argparse.ArgumentParser(description = 'Keysigning Assistance and Notifying Tool (KANT)',
|
args = argparse.ArgumentParser(description = 'Keysigning Assistance and Notifying Tool (KANT)',
|
||||||
epilog = 'brent s. || 2017 || https://square-r00t.net',
|
epilog = 'brent s. || 2017 || https://square-r00t.net')
|
||||||
formatter_class = argparse.RawTextHelpFormatter)
|
|
||||||
args.add_argument('-k',
|
args.add_argument('-k',
|
||||||
'--keys',
|
'--keys',
|
||||||
dest = 'keys',
|
dest = 'keys',
|
||||||
metavar = 'KEYS | /path/to/batchfile',
|
metavar = 'KEYS | /path/to/batchfile',
|
||||||
required = True,
|
required = True,
|
||||||
help = 'A single or comma-separated list of keys to sign,\n' +
|
help = 'A single/comma-separated list of keys to sign, ' +
|
||||||
'trust, and notify. Can also be an email address.\n' +
|
'trust, & notify. Can also be an email address. ' +
|
||||||
'If -b/--batch is specified, this should instead be\n' +
|
'If -b/--batch is specified, this should instead be ' +
|
||||||
'a path to the batch file.')
|
'a path to the batch file. See the man page for more info.')
|
||||||
args.add_argument('-K',
|
args.add_argument('-K',
|
||||||
'--sigkey',
|
'--sigkey',
|
||||||
dest = 'sigkey',
|
dest = 'sigkey',
|
||||||
default = defkey,
|
default = defkey,
|
||||||
help = 'The key to use when signing other keys.\nDefault is \033[1m{0}\033[0m.'.format(defkey))
|
help = 'The key to use when signing other keys. Default is \033[1m{0}\033[0m.'.format(defkey))
|
||||||
args.add_argument('-t',
|
args.add_argument('-t',
|
||||||
'--trust',
|
'--trust',
|
||||||
dest = 'trustlevel',
|
dest = 'trustlevel',
|
||||||
default = None,
|
default = None,
|
||||||
help = 'The trust level to automatically apply to all keys\n' +
|
help = 'The trust level to automatically apply to all keys ' +
|
||||||
'(if not specified, kant will prompt for each key).\n' +
|
'(if not specified, kant will prompt for each key). ' +
|
||||||
'See -b/--batch for trust level notations.')
|
'See BATCHFILE/TRUSTLEVEL in the man page for trust ' +
|
||||||
|
'level notations.')
|
||||||
args.add_argument('-c',
|
args.add_argument('-c',
|
||||||
'--check',
|
'--check',
|
||||||
dest = 'checklevel',
|
dest = 'checklevel',
|
||||||
default = None,
|
default = None,
|
||||||
help = 'The level of checking done (if not specified, kant will\n' +
|
help = 'The level of checking done (if not specified, kant will ' +
|
||||||
'prompt for each key). See -b/--batch for check level notations.')
|
'prompt for each key). See -b/--batch for check level notations.')
|
||||||
args.add_argument('-e',
|
|
||||||
'--export',
|
|
||||||
dest = 'export',
|
|
||||||
default = 'true',
|
|
||||||
help = 'Make the signatures exportable (default is True).\nSee -b/--batch for more information.')
|
|
||||||
args.add_argument('-l',
|
args.add_argument('-l',
|
||||||
'--local',
|
'--local',
|
||||||
dest = 'local',
|
dest = 'local',
|
||||||
default = 'false',
|
default = 'false',
|
||||||
help = 'Make the signature(s) local-only (i.e. don\'t push to a keyserver).')
|
help = 'Make the signature(s) local-only (i.e. don\'t push to a keyserver).')
|
||||||
|
args.add_argument('-n',
|
||||||
|
'--no-notify',
|
||||||
|
dest = 'notify',
|
||||||
|
action = 'store_false',
|
||||||
|
help = 'If specified, do NOT notify any key recipients that you\'ve signed ' +
|
||||||
|
'their key, even if KANT is able to.')
|
||||||
args.add_argument('-s',
|
args.add_argument('-s',
|
||||||
'--keyservers',
|
'--keyservers',
|
||||||
dest = 'keyservers',
|
dest = 'keyservers',
|
||||||
default = defkeyservers,
|
default = defkeyservers,
|
||||||
help = 'The comma-separated keyserver(s) to push to.\n' +
|
help = 'The comma-separated keyserver(s) to push to.\n' +
|
||||||
'Default keyserver list is: \n\n\t\033[1m{0}\033[0m\n\n'.format(re.sub(',', '\n\t', defkeyservers)))
|
'Default keyserver list is: \n\n\t\033[1m{0}\033[0m\n\n'.format(re.sub(',',
|
||||||
# This will require some restructuring...
|
'\n\t',
|
||||||
|
defkeyservers)))
|
||||||
args.add_argument('-b',
|
args.add_argument('-b',
|
||||||
'--batch',
|
'--batch',
|
||||||
dest = 'batch',
|
dest = 'batch',
|
||||||
action = 'store_true',
|
action = 'store_true',
|
||||||
help = 'If specified, -k/--keys is a CSV file to use as a\n' +
|
help = 'If specified, -k/--keys is a CSV file to use as a ' +
|
||||||
'batch run in the format of (one per line):\n' +
|
'batch run. See the BATCHFILE section in the man page for more info.')
|
||||||
'\n\033[1mKEY_ID,TRUSTLEVEL,PUSH,CHECKLEVEL,EXPORT\033[0m\n'
|
args.add_argument('-D',
|
||||||
'\n\033[1mKEY_ID\033[0m can be the full 40-char key ID (fingerprint)\n' +
|
|
||||||
'or an email address of the key.\n\n\033[1mTRUSTLEVEL\033[0m is how much trust to assign, and can\n' +
|
|
||||||
'be numeric or string:' +
|
|
||||||
'\n\n\t\033[1m-1 = Never\n\t 0 = Unknown\n\t 1 = Untrusted\n\t 2 = Marginal\n\t 3 = Full\n\t 4 = Ultimate\033[0m\n' +
|
|
||||||
'\n\033[1mPUSH\033[0m can be \033[1m1/True\033[0m or \033[1m0/False\033[0m.\n' +
|
|
||||||
'If marked as False, the signature will be made local.\n' +
|
|
||||||
'\n\033[1mCHECKLEVEL\033[0m is the amount of checking done on the owner\'s\n' +
|
|
||||||
'validity of identity. Can be numeric or string:' +
|
|
||||||
'\n\n\t\033[1m 0 = Unknown\n\t 1 = None\n\t 2 = Casual\n\t 3 = Careful\033[0m\n' +
|
|
||||||
'\n\033[1mEXPORT\033[0m can be either \033[1m1/True\033[0m or \033[1m0/False\033[0m.\n' +
|
|
||||||
'If True, make the signature exportable.\nIf False, make it non-exportable.')
|
|
||||||
args.add_argument('-d',
|
|
||||||
'--gpgdir',
|
'--gpgdir',
|
||||||
dest = 'gpgdir',
|
dest = 'gpgdir',
|
||||||
default = defgpgdir,
|
default = defgpgdir,
|
||||||
@ -533,12 +541,12 @@ def verifyArgs(args):
|
|||||||
raise NotADirectoryError('{0} is not a directory'.format(args['gpgdir']))
|
raise NotADirectoryError('{0} is not a directory'.format(args['gpgdir']))
|
||||||
try:
|
try:
|
||||||
os.environ['GNUPGHOME'] = args['gpgdir']
|
os.environ['GNUPGHOME'] = args['gpgdir']
|
||||||
gpg = gpgme.Context()
|
ctx = gpg.Context()
|
||||||
except:
|
except:
|
||||||
raise RuntimeError('Could not use {0} as a GnuPG home'.format(args['gpgdir']))
|
raise RuntimeError('Could not use {0} as a GnuPG home'.format(args['gpgdir']))
|
||||||
# Now we need to verify that the private key exists...
|
# Now we need to verify that the private key exists...
|
||||||
try:
|
try:
|
||||||
sigkey = gpg.get_key(args['sigkey'], True)
|
sigkey = ctx.get_key(args['sigkey'], True)
|
||||||
except GpgmeError:
|
except GpgmeError:
|
||||||
raise ValueError('Cannot use key {0}'.format(args['sigkey']))
|
raise ValueError('Cannot use key {0}'.format(args['sigkey']))
|
||||||
# And that it is an eligible candidate to use to sign.
|
# And that it is an eligible candidate to use to sign.
|
||||||
@ -548,12 +556,12 @@ def verifyArgs(args):
|
|||||||
if args['testkeyservers']:
|
if args['testkeyservers']:
|
||||||
for s in args['keyservers']:
|
for s in args['keyservers']:
|
||||||
# Test to make sure the keyserver is accessible.
|
# Test to make sure the keyserver is accessible.
|
||||||
# First we need to construct a way to use python's socket connector
|
v6test = socket(AF_INET6, SOCK_DGRAM)
|
||||||
# Great. Now we need to just quickly check to make sure it's accessible - if specified.
|
try:
|
||||||
if args['netproto'] == '4':
|
v6test.connect(('ipv6.square-r00t.net', 0))
|
||||||
nettype = AF_INET
|
nettype = AF_INET6 # We have IPv6 intarwebz
|
||||||
elif args['netproto'] == '6':
|
except:
|
||||||
nettype = AF_INET6
|
nettype = AF_INET # No IPv6, default to IPv4
|
||||||
for proto in s['port'][1]:
|
for proto in s['port'][1]:
|
||||||
if proto == 'udp':
|
if proto == 'udp':
|
||||||
netproto = SOCK_DGRAM
|
netproto = SOCK_DGRAM
|
||||||
@ -564,7 +572,7 @@ def verifyArgs(args):
|
|||||||
tests = sock.connect_ex((s['server'], int(s['port'][0])))
|
tests = sock.connect_ex((s['server'], int(s['port'][0])))
|
||||||
uristr = '{0}://{1}:{2} ({3})'.format(s['proto'], s['server'], s['port'][0], proto.upper())
|
uristr = '{0}://{1}:{2} ({3})'.format(s['proto'], s['server'], s['port'][0], proto.upper())
|
||||||
if not tests == 0:
|
if not tests == 0:
|
||||||
raise RuntimeError('Keyserver {0} is not available'.format(uristr))
|
raise OSError('Keyserver {0} is not available'.format(uristr))
|
||||||
else:
|
else:
|
||||||
print('Keyserver {0} is accepting connections.'.format(uristr))
|
print('Keyserver {0} is accepting connections.'.format(uristr))
|
||||||
sock.close()
|
sock.close()
|
||||||
@ -573,11 +581,12 @@ def verifyArgs(args):
|
|||||||
def main():
|
def main():
|
||||||
rawargs = parseArgs()
|
rawargs = parseArgs()
|
||||||
args = verifyArgs(vars(rawargs.parse_args()))
|
args = verifyArgs(vars(rawargs.parse_args()))
|
||||||
modifyDirmngr('new', args)
|
sess = sigsession(args)
|
||||||
fprs = getKeys(args)
|
sess.modifyDirmngr('new')
|
||||||
trusts = trustKeys(fprs, args)
|
sess.getKeys()
|
||||||
sigs = sigKeys(trusts, args)
|
sess.trustKeys()
|
||||||
modifyDirmngr('old', args)
|
sess.sigKeys()
|
||||||
|
sess.modifyDirmngr('old')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
62
gpg/kant/test.py
Executable file
62
gpg/kant/test.py
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# This is more of a documentation on some python-gpgme (https://pypi.python.org/pypi/gpg) examples.
|
||||||
|
# Because their only documentation for the python bindings is in pydoc, and the C API manual is kind of useless.
|
||||||
|
|
||||||
|
import gpg
|
||||||
|
import gpg.constants
|
||||||
|
import inspect
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
# my key ID
|
||||||
|
mykey = '748231EBCBD808A14F5E85D28C004C2F93481F6B'
|
||||||
|
# a key to test with
|
||||||
|
theirkey = '63D1CEA387C27A92E0D50AB8343C305F9109D4DC'
|
||||||
|
|
||||||
|
# Create a context
|
||||||
|
# Params:
|
||||||
|
#armor -- enable ASCII armoring (default False)
|
||||||
|
#textmode -- enable canonical text mode (default False)
|
||||||
|
#offline -- do not contact external key sources (default False)
|
||||||
|
#signers -- list of keys used for signing (default [])
|
||||||
|
#pinentry_mode -- pinentry mode (default PINENTRY_MODE_DEFAULT)
|
||||||
|
#protocol -- protocol to use (default PROTOCOL_OpenPGP)
|
||||||
|
#home_dir -- state directory (default is the engine default)
|
||||||
|
ctx = gpg.Context()
|
||||||
|
|
||||||
|
# Fetch a key from the keyring
|
||||||
|
#secret -- to request a secret key
|
||||||
|
mkey = ctx.get_key(mykey)
|
||||||
|
tkey = ctx.get_key(theirkey)
|
||||||
|
|
||||||
|
## Print the attributes of our key and other info
|
||||||
|
##https://stackoverflow.com/a/41737776
|
||||||
|
##for k in (mkey, tkey):
|
||||||
|
#for k in [mkey]:
|
||||||
|
# for i in inspect.getmembers(k):
|
||||||
|
# if not i[0].startswith('_'):
|
||||||
|
# pprint.pprint(i)
|
||||||
|
#pprint.pprint(ctx.get_engine_info())
|
||||||
|
|
||||||
|
# Print the constants
|
||||||
|
#pprint.pprint(inspect.getmembers(gpg.constants))
|
||||||
|
|
||||||
|
# Get remote key. Use an OR to search both keyserver and local.
|
||||||
|
#ctx.set_keylist_mode(gpg.constants.KEYLIST_MODE_EXTERN|gpg.constants.KEYLIST_MODE_LOCAL)
|
||||||
|
klmodes = {'local': gpg.constants.KEYLIST_MODE_LOCAL,
|
||||||
|
'remote': gpg.constants.KEYLIST_MODE_EXTERN,
|
||||||
|
'both': gpg.constants.KEYLIST_MODE_LOCAL|gpg.constants.KEYLIST_MODE_EXTERN}
|
||||||
|
|
||||||
|
# List keys
|
||||||
|
#pattern -- return keys matching pattern (default: all keys)
|
||||||
|
#secret -- return only secret keys (default: False)
|
||||||
|
#mode -- keylist mode (default: list local keys)
|
||||||
|
#source -- read keys from source instead from the keyring
|
||||||
|
# (all other options are ignored in this case)
|
||||||
|
ctx.keylist(pattern = 'bts@square-r00t.net',
|
||||||
|
secret = False,
|
||||||
|
mode = klmodes['both'],
|
||||||
|
source = None)
|
||||||
|
|
||||||
|
# Test fetching from a keyserver
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
748231EBCBD808A14F5E85D28C004C2F93481F6B,4,1
|
748231EBCBD808A14F5E85D28C004C2F93481F6B,4,1,3,1
|
||||||
A03CACFD7123AF443A3A185298A8A46921C8DDEF,-1,-1
|
A03CACFD7123AF443A3A185298A8A46921C8DDEF,-1,0,0,0
|
||||||
EFD9413B17293AFDFE6EA6F1402A088DEDF104CB,full,true
|
EFD9413B17293AFDFE6EA6F1402A088DEDF104CB,full,true,casual,yes
|
||||||
6FA8AE12AEC90B035EEE444FE70457341A63E830,2,True
|
6FA8AE12AEC90B035EEE444FE70457341A63E830,2,True,Casual,True
|
||||||
<admin@sysadministrivia.com>, full, yes
|
<admin@sysadministrivia.com>, full, yes, careful, false
|
||||||
|
|
@ -42,7 +42,7 @@ sks = {
|
|||||||
# I would hope this is self-explanatory. If not, this is where we log the outout of the sks dump process. (and any rsync errors, too)
|
# I would hope this is self-explanatory. If not, this is where we log the outout of the sks dump process. (and any rsync errors, too)
|
||||||
'logfile': '/var/log/sksdump.log',
|
'logfile': '/var/log/sksdump.log',
|
||||||
# If not None value, where we should push the dumps when done. Can be a local path too, obviously.
|
# If not None value, where we should push the dumps when done. Can be a local path too, obviously.
|
||||||
'rsync': 'root@sks.mirror.square-r00t.net:/srv/http/sks/dumps/.',
|
'rsync': 'root@mirror.square-r00t.net:/srv/http/sks/dumps/.',
|
||||||
# How many previous days of dumps should we keep?
|
# How many previous days of dumps should we keep?
|
||||||
'days': 1,
|
'days': 1,
|
||||||
# How many keys to include per dump file
|
# How many keys to include per dump file
|
||||||
|
Loading…
Reference in New Issue
Block a user