diff options
Diffstat (limited to 'lang/python/docs/dita/howto/part04')
11 files changed, 562 insertions, 0 deletions
diff --git a/lang/python/docs/dita/howto/part04/basic-functions.dita b/lang/python/docs/dita/howto/part04/basic-functions.dita new file mode 100644 index 00000000..a0c64b56 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/basic-functions.dita @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_w5f_zpy_5db"> + <title>Basic Functions</title> + <body> + <p>The most frequently called features of any cryptographic library will be the most + fundamental tasks for encryption software. In this section we will look at how to + programmatically encrypt data, decrypt it, sign it and verify signatures.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/clear-signing.dita b/lang/python/docs/dita/howto/part04/clear-signing.dita new file mode 100644 index 00000000..c6104922 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/clear-signing.dita @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_ydy_5qz_5db"> + <title>Clear Signatures</title> + <body> + <p>Though PGP/in-line messages are no longer encouraged in favour of PGP/MIME, there is still + sometimes value in utilising in-line signatures. This is where clear-signed messages or text + is of value.</p> + <p> + <codeblock id="clrsig-1" outputclass="language-python">import gpg + +text0 = """Declaration of ... something. + +""" +text = text0.encode() + +c = gpg.Context() +signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR) + +with open("/path/to/statement.txt.asc", "w") as afile: + afile.write(signed_data.decode()) +</codeblock> + </p> + <p>In spite of the appearance of a clear-signed message, the data handled by GPGME in signing + it must still be byte literals.</p> + <p> + <codeblock id="clrsig-2" outputclass="language-python">import gpg + +with open("/path/to/statement.txt", "rb") as tfile: + text = tfile.read() + +c = gpg.Context() +signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR) + +with open("/path/to/statement.txt.asc", "wb") as afile: + afile.write(signed_data) +</codeblock> + </p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/decryption.dita b/lang/python/docs/dita/howto/part04/decryption.dita new file mode 100644 index 00000000..e3918c55 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/decryption.dita @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_vqx_tqy_5db"> + <title>Decryption</title> + <body> + <p>Decrypting something encrypted to a key in one's secret keyring is fairly straight + forward.</p> + <p>In this example code, however, preconfiguring either <codeph>gpg.Context()</codeph> or + <codeph>gpg.core.Context()</codeph> as <codeph>c</codeph> is unnecessary because there is + no need to modify the Context prior to conducting the decryption and since the Context is + only used once, setting it to c simply adds lines for no gain.</p> + <p> + <codeblock id="decry-1" outputclass="language-python">import gpg + +ciphertext = input("Enter path and filename of encrypted file: ") +newfile = input("Enter path and filename of file to save decrypted data to: ") + +with open(ciphertext, "rb") as cfile: + plaintext, result, verify_result = gpg.Context().decrypt(cfile) + +with open(newfile, "wb") as nfile: + nfile.write(plaintext) +</codeblock> + </p> + <p>The data available in <codeph>plaintext</codeph> in this example is the decrypted content + as a byte object, the recipient key IDs and algorithms in <codeph>result</codeph> and the + results of verifying any signatures of the data in <codeph>verify_result</codeph>.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/default-signing.dita b/lang/python/docs/dita/howto/part04/default-signing.dita new file mode 100644 index 00000000..d3a227b5 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/default-signing.dita @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_ntx_sqz_5db"> + <title>Default Signatures</title> + <body> + <p>The normal or default signing process is essentially the same as is most often + invoked when also encrypting a message or file. So when the encryption component is not + utilised, the result is to produce an encoded and signed output which may or may not be + ASCII armoured and which may or may not also be compressed.</p> + <p>By default compression will be used unless GnuPG detects that the plaintext is already + compressed. ASCII armouring will be determined according to the value of + <codeph>gpg.Context().armor</codeph>.</p> + <p>The compression algorithm is selected in much the same way as the symmetric encryption + algorithm or the hash digest algorithm is when multiple keys are involved; from the + preferences saved into the key itself or by comparison with the preferences with all other + keys involved.</p> + <p> + <codeblock id="defsig-1" outputclass="language-python">import gpg + +text0 = """Declaration of ... something. + +""" +text = text0.encode() + +c = gpg.Context(armor=True, signers=sig_src) +signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL) + +with open("/path/to/statement.txt.asc", "w") as afile: + afile.write(signed_data.decode()) +</codeblock> + </p> + <p>Though everything in this example is accurate, it is more likely that reading the input + data from another file and writing the result to a new file will be performed more like the + way it is done in the next example. Even if the output format is ASCII armoured.</p> + <p> + <codeblock id="defsig-2" outputclass="language-python">import gpg + +with open("/path/to/statement.txt", "rb") as tfile: + text = tfile.read() + +c = gpg.Context() +signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL) + +with open("/path/to/statement.txt.sig", "wb") as afile: + afile.write(signed_data) +</codeblock> + </p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/detached-signing.dita b/lang/python/docs/dita/howto/part04/detached-signing.dita new file mode 100644 index 00000000..38406eff --- /dev/null +++ b/lang/python/docs/dita/howto/part04/detached-signing.dita @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_rfg_5qz_5db"> + <title>Detached Signatures</title> + <body> + <p>Detached signatures will often be needed in programmatic uses of GPGME, either for signing + files (e.g. tarballs of code releases) or as a component of message signing (e.g. PGP/MIME + encoded email).</p> + <p> + <codeblock id="detsig-1" outputclass="language-python">import gpg + +text0 = """Declaration of ... something. + +""" +text = text0.encode() + +c = gpg.Context(armor=True) +signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH) + +with open("/path/to/statement.txt.asc", "w") as afile: + afile.write(signed_data.decode()) +</codeblock> + </p> + <p>As with normal signatures, detached signatures are best handled as byte literals, even when + the output is ASCII armoured.</p> + <p> + <codeblock id="detsig-2" outputclass="language-python">import gpg + +with open("/path/to/statement.txt", "rb") as tfile: + text = tfile.read() + +c = gpg.Context(signers=sig_src) +signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH) + +with open("/path/to/statement.txt.sig", "wb") as afile: + afile.write(signed_data) +</codeblock> + </p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/encrypt-to-many.dita b/lang/python/docs/dita/howto/part04/encrypt-to-many.dita new file mode 100644 index 00000000..df3454f8 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/encrypt-to-many.dita @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_wmg_tjz_5db"> + <title>Encrypting to Multiple Keys</title> + <body> + <p>Encrypting to multiple keys essentially just expands upon the key selection process + and the recipients from the previous examples.</p> + <p>The following example encrypts a message (<codeph>text</codeph>) to everyone with an email + address on the <codeph>gnupg.org </codeph>domain,<fn>You probably don't really want to do + this. Searching the keyservers for "gnupg.org" produces over 400 results, the majority of + which aren't actually at the gnupg.org domain, but just included a comment regarding the + project in their key somewhere.</fn> but does <i>not</i> encrypt to a default key or other + key which is configured to normally encrypt to.</p> + <p> + <codeblock id="enc2-1" outputclass="language-python">import gpg + +text = b"""Oh look, another test message. + +The same rules apply as with the previous example and more likely +than not, the message will actually be drawn from reading the +contents of a file or, maybe, from entering data at an input() +prompt. + +Since the text in this case must be bytes, it is most likely that +the input form will be a separate file which is opened with "rb" +as this is the simplest method of obtaining the correct data +format. +""" + +c = gpg.Context(armor=True) +rpattern = list(c.keylist(pattern="@gnupg.org", secret=False)) +logrus = [] + +for i in range(len(rpattern)): + if rpattern[i].can_encrypt == 1: + logrus.append(rpattern[i]) + +ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, sign=False, + always_trust=True) + +with open("secret_plans.txt.asc", "wb") as f: + f.write(ciphertext) +</codeblock> + </p> + <p>All it would take to change the above example to sign the message and also encrypt the + message to any configured default keys would be to change the <codeph>c.encrypt</codeph> + line to this:</p> + <p> + <codeblock id="enc2-2" outputclass="language-python">ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, + always_trust=True, + add_encrypt_to=True) +</codeblock> + </p> + <p>The only keyword arguments requiring modification are those for which the default values + are changing. The default value of <codeph>sign</codeph> is <codeph>True</codeph>, the + default of <codeph>always_trust</codeph> is <codeph>False</codeph>, the default of + <codeph>add_encrypt_to</codeph> is <codeph>False</codeph>.</p> + <p>If <codeph>always_trust</codeph> is not set to <codeph>True</codeph> and any of the + recipient keys are not trusted (e.g. not signed or locally signed) then the encryption will + raise an error. It is possible to mitigate this somewhat with something more like this:</p> + <p> + <codeblock id="enc2-3" outputclass="language-python">import gpg + +with open("secret_plans.txt.asc", "rb") as f: + text = f.read() + +c = gpg.Context(armor=True) +rpattern = list(c.keylist(pattern="@gnupg.org", secret=False)) +logrus = [] + +for i in range(len(rpattern)): + if rpattern[i].can_encrypt == 1: + logrus.append(rpattern[i]) + +try: + ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, + add_encrypt_to=True) +except gpg.errors.InvalidRecipients as e: + for i in range(len(e.recipients)): + for n in range(len(logrus)): + if logrus[n].fpr == e.recipients[i].fpr: + logrus.remove(logrus[n]) + else: + pass + try: + ciphertext, result, sign_result = c.encrypt(text, recipients=logrus, + add_encrypt_to=True) + except: + pass + +with open("secret_plans.txt.asc", "wb") as f: + f.write(ciphertext) +</codeblock> + </p> + <p>This will attempt to encrypt to all the keys searched for, then remove invalid recipients + if it fails and try again.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/encrypt-to-one.dita b/lang/python/docs/dita/howto/part04/encrypt-to-one.dita new file mode 100644 index 00000000..2abbe06a --- /dev/null +++ b/lang/python/docs/dita/howto/part04/encrypt-to-one.dita @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_dkk_sjz_5db"> + <title>Encrypting to One Key</title> + <body> + <p>Once the the Context is set the main issues with encrypting data is essentially reduced to + key selection and the keyword arguments specified in the + <codeph>gpg.Context().encrypt()</codeph> method.</p> + <p>Those keyword arguments are:</p> + <p> + <ul id="ul_cmt_3kz_5db"> + <li><codeph>recipients</codeph>, a list of keys encrypted to (covered in greater detail in + the following section);</li> + <li><codeph>sign</codeph>, whether or not to sign the plaintext data, see subsequent + sections on signing and verifying signatures below (defaults to + <codeph>True</codeph>);</li> + <li><codeph>sink</codeph>, to write results or partial results to a secure sink instead of + returning it (defaults to <codeph>None</codeph>);</li> + <li><codeph>passphrase</codeph>, only used when utilising symmetric encryption (defaults + to <codeph>None</codeph>);</li> + <li><codeph>always_trust</codeph>, used to override the trust model settings for recipient + keys (defaults to <codeph>False</codeph>);</li> + <li><codeph>add_encrypt_to</codeph>, utilises any preconfigured encrypt-to or default-key + settings in the user's <filepath>gpg.conf</filepath> file (defaults to + <codeph>False</codeph>);</li> + <li><codeph>prepare</codeph>, prepare for encryption (defaults to + <codeph>False</codeph>);</li> + <li><codeph>expect_sign</codeph>, prepare for signing (defaults to + <codeph>False</codeph>);</li> + <li><codeph>compress</codeph>, compresses the plaintext prior to encryption (defaults to + <codeph>True</codeph>).</li> + </ul> + </p> + <p> + <codeblock id="enc1-1" outputclass="language-python">import gpg + +a_key = "0x12345678DEADBEEF" +text = b"""Some text to test with. + +Since the text in this case must be bytes, it is most likely that +the input form will be a separate file which is opened with "rb" +as this is the simplest method of obtaining the correct data +format. +""" + +c = gpg.Context(armor=True) +rkey = list(c.keylist(pattern=a_key, secret=False)) +ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False) + +with open("secret_plans.txt.asc", "wb") as f: + f.write(ciphertext) +</codeblock> + </p> + <p>Though this is even more likely to be used like this; with the plaintext input read from a + file, the recipient keys used for encryption regardless of key trust status and the + encrypted output also encrypted to any preconfigured keys set in the + <filepath>gpg.conf</filepath> file:</p> + <p> + <codeblock id="enc1-2" outputclass="language-python">import gpg + +a_key = "0x12345678DEADBEEF" + +with open("secret_plans.txt", "rb") as f: + text = f.read() + +c = gpg.Context(armor=True) +rkey = list(c.keylist(pattern=a_key, secret=False)) +ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, + sign=True, always_trust=True, + add_encrypt_to=True) + +with open("secret_plans.txt.asc", "wb") as f: + f.write(ciphertext) +</codeblock> + </p> + <p>If the <codeph>recipients</codeph> paramater is empty then the plaintext is encrypted + symmetrically. If no <codeph>passphrase</codeph> is supplied as a parameter or via a + callback registered with the <codeph>Context()</codeph> then an out-of-band prompt for the + passphrase via pinentry will be invoked.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/encryption.dita b/lang/python/docs/dita/howto/part04/encryption.dita new file mode 100644 index 00000000..572cc9d2 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/encryption.dita @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_on2_nqy_5db"> + <title>Encryption</title> + <body> + <p>Encrypting is very straight forward. In the first example below the message, + <codeph>text</codeph>, is encrypted to a single recipient's key. In the second example the + message will be encrypted to multiple recipients.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/signing-key-selection.dita b/lang/python/docs/dita/howto/part04/signing-key-selection.dita new file mode 100644 index 00000000..34d02b4a --- /dev/null +++ b/lang/python/docs/dita/howto/part04/signing-key-selection.dita @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_dpb_fqz_5db"> + <title>Signing Key Selection</title> + <body> + <p>By default GPGME and the Python bindings will use the default key configured for the user + invoking the GPGME API. If there is no default key specified and there is more than one + secret key available it may be necessary to specify the key or keys with which to sign + messages and files.</p> + <p> + <codeblock id="sigkey-1" outputclass="language-python">import gpg + +logrus = input("Enter the email address or string to match signing keys to: ") +hancock = gpg.Context().keylist(pattern=logrus, secret=True) +sig_src = list(hancock) +</codeblock> + </p> + <p>The signing examples in the following sections include the explicitly designated + <codeph>signers</codeph> parameter in two of the five examples; once where the resulting + signature would be ASCII armoured and once where it would not be armoured.</p> + <p>While it would be possible to enter a key ID or fingerprint here to match a specific key, + it is not possible to enter two fingerprints and match two keys since the patten expects a + string, bytes or None and not a list. A string with two fingerprints won't match any single + key.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/signing.dita b/lang/python/docs/dita/howto/part04/signing.dita new file mode 100644 index 00000000..289e3742 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/signing.dita @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_nqk_vqy_5db"> + <title>Signing Text and Files</title> + <body> + <p>The following sections demonstrate how to specify keys to sign with and the types of + signatures which can be made.</p> + </body> + </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part04/verification.dita b/lang/python/docs/dita/howto/part04/verification.dita new file mode 100644 index 00000000..d50482a8 --- /dev/null +++ b/lang/python/docs/dita/howto/part04/verification.dita @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> + <topic id="topic_p3g_yqy_5db"> + <title>Signature Verification</title> + <body> + <p>Essentially there are two principal methods of verification of a signature. The first + of these is for use with the normal or default signing method and for clear-signed messages. + The second is for use with files and data with detached signatures.</p> + <p>The following example is intended for use with the default signing method where the file + was not ASCII armoured:</p> + <p> + <codeblock id="verify-1" outputclass="language-python">import gpg +import time + +filename = "statement.txt" +gpg_file = "statement.txt.gpg" + +c = gpg.Context() + +try: + data, result = c.verify(open(gpg_file)) + verified = True +except gpg.errors.BadSignatures as e: + verified = False + print(e) + +if verified is True: + for i in range(len(result.signatures)): + sign = result.signatures[i] + print("""Good signature from: +{0} +with key {1} +made at {2} +""".format(c.get_key(sign.fpr).uids[0].uid, + sign.fpr, time.ctime(sign.timestamp))) +else: + pass +</codeblock> + </p> + <p>Whereas this next example, which is almost identical would work with normal ASCII armoured + files and with clear-signed files:</p> + <p> + <codeblock id="verify-2" outputclass="language-python">import gpg +import time + +filename = "statement.txt" +asc_file = "statement.txt.asc" + +c = gpg.Context() + +try: + data, result = c.verify(open(asc_file)) + verified = True +except gpg.errors.BadSignatures as e: + verified = False + print(e) + +if verified is True: + for i in range(len(result.signatures)): + sign = result.signatures[i] + print("""Good signature from: +{0} +with key {1} +made at {2} +""".format(c.get_key(sign.fpr).uids[0].uid, + sign.fpr, time.ctime(sign.timestamp))) +else: + pass +</codeblock> + </p> + <p>In both of the previous examples it is also possible to compare the original data that was + signed against the signed data in <codeph>data</codeph> to see if it matches with something + like this:</p> + <p> + <codeblock id="verify-3" outputclass="language-python">with open(filename, "rb") as afile: + text = afile.read() + +if text == data: + print("Good signature.") +else: + pass +</codeblock> + </p> + <p>The following two examples, however, deal with detached signatures. With his method of + verification the data that was signed does not get returned since it is already being + explicitly referenced in the first argument of <codeph>c.verify</codeph>. So + <codeph>data</codeph> is <codeph>None</codeph> and only the information in + <codeph>result</codeph> is available.</p> + <p> + <codeblock id="verify-4" outputclass="language-python">import gpg +import time + +filename = "statement.txt" +sig_file = "statement.txt.sig" + +c = gpg.Context() + +try: + data, result = c.verify(open(filename), open(sig_file)) + verified = True +except gpg.errors.BadSignatures as e: + verified = False + print(e) + +if verified is True: + for i in range(len(result.signatures)): + sign = result.signatures[i] + print("""Good signature from: +{0} +with key {1} +made at {2} +""".format(c.get_key(sign.fpr).uids[0].uid, + sign.fpr, time.ctime(sign.timestamp))) +else: + pass +</codeblock> + </p> + <p> + <codeblock id="verify-5" outputclass="language-python">import gpg +import time + +filename = "statement.txt" +asc_file = "statement.txt.asc" + +c = gpg.Context() + +try: + data, result = c.verify(open(filename), open(asc_file)) + verified = True +except gpg.errors.BadSignatures as e: + verified = False + print(e) + +if verified is not None: + for i in range(len(result.signatures)): + sign = result.signatures[i] + print("""Good signature from: +{0} +with key {1} +made at {2} +""".format(c.get_key(sign.fpr).uids[0].uid, + sign.fpr, time.ctime(sign.timestamp))) +else: + pass +</codeblock> + </p> + </body> + </topic> +</dita> |