diff options
| author | Ben McGinnes <[email protected]> | 2018-05-15 03:13:16 +0000 | 
|---|---|---|
| committer | Ben McGinnes <[email protected]> | 2018-05-15 03:13:16 +0000 | 
| commit | f0063afa71bc7e71f19d174acc2fde26f0c11850 (patch) | |
| tree | a23a33ef70f13747642a8c96e7128e41b5f7ce58 /lang/python/docs/dita | |
| parent | json: Improve auto-base64 encoding to not split UTF-8 chars. (diff) | |
| download | gpgme-f0063afa71bc7e71f19d174acc2fde26f0c11850.tar.gz gpgme-f0063afa71bc7e71f19d174acc2fde26f0c11850.zip | |
docs: python bindings HOWTO - DITA XML version
* Due to the org-babel bug which breaks Python source code examples
  beyond the most simple snippets, ported the HOWTO to a source format
  which I *know* for sure won't break it.
* Details of the org-mode bug is in https://dev.gnupg.org/T3977
* DITA project uses DITA-OT 2.x (2.4 or 2.5, IIRC) with support for DITA 1.3.
* source files were written with oXygenXML Editor 20.0, hence the
  oXygenXML project file in the directory; however only the .ditamap
  and .dita files are required to generate any output with the
  DITA-OT.
Signed-off-by: Ben McGinnes <[email protected]>
Diffstat (limited to '')
49 files changed, 1927 insertions, 0 deletions
| diff --git a/lang/python/docs/dita/gpgme-python-howto.ditamap b/lang/python/docs/dita/gpgme-python-howto.ditamap new file mode 100644 index 00000000..99dde6ce --- /dev/null +++ b/lang/python/docs/dita/gpgme-python-howto.ditamap @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE bookmap PUBLIC "-//OASIS//DTD DITA BookMap//EN" "bookmap.dtd"> +<bookmap> +  <booktitle> +    <mainbooktitle>GPGME Python Bindings HOWTO</mainbooktitle> +  </booktitle> +  <part id="part-1" href="howto/part-1.dita"> +    <topicref id="intro" href="howto/part01/introduction.dita"> +      <topicref id="py2v3" href="howto/part01/python2-vs-python3.dita"/> +      <topicref id="examples" href="howto/part01/examples.dita"/> +    </topicref> +    <topicref id="docsrc" href="howto/part01/docs-source.dita"/> +  </part> +  <part id="part-2" href="howto/part-2.dita"> +    <topicref id="gpgme-concepts" href="howto/part02/gpgme-concepts.dita"> +      <topicref id="c-api" href="howto/part02/c-api.dita"/> +      <topicref id="python-bindings" href="howto/part02/python-bindings.dita"/> +      <topicref id="py-diffs" href="howto/part02/differences-to-others.dita"> +        <topicref id="vinay-sajip" href="howto/part02/python-gnupg.dita"/> +        <topicref id="isis-lovecruft" href="howto/part02/daesh.dita"/> +        <topicref id="martin-albrecht" href="howto/part02/pyme.dita"/> +      </topicref> +    </topicref> +    <topicref id="install" href="howto/part02/installation.dita"> +      <topicref id="no-pypi" href="howto/part02/no-pypi.dita"/> +      <topicref id="requirements" href="howto/part02/requirements.dita"/> +      <topicref id="installing" href="howto/part02/installing.dita"/> +    </topicref> +    <topicref id="fundamentals" href="howto/part02/fundamentals.dita"> +      <topicref id="for-the-wicked" href="howto/part02/no-rest.dita"/> +      <topicref id="excession" href="howto/part02/context.dita"/> +    </topicref> +  </part> +  <part id="part-3" href="howto/part-3.dita"> +    <topicref id="key-select" href="howto/part03/key-selection.dita"> +      <topicref id="key-count" href="howto/part03/key-counting.dita"/> +    </topicref> +    <topicref id="get-key" href="howto/part03/get-key.dita"/> +  </part> +  <part id="part-4" href="howto/part-4.dita"> +    <topicref id="basics" href="howto/part04/basic-functions.dita"/> +    <topicref id="encryption" href="howto/part04/encryption.dita"> +      <topicref id="encrypt-to-one" href="howto/part04/encrypt-to-one.dita"/> +      <topicref id="encrypt-to-many" href="howto/part04/encrypt-to-many.dita"/> +    </topicref> +    <topicref id="decryption" href="howto/part04/decryption.dita"/> +    <topicref id="signing" href="howto/part04/signing.dita"> +      <topicref id="sign-key-select" href="howto/part04/signing-key-selection.dita"/> +      <topicref id="signing-default" href="howto/part04/default-signing.dita"/> +      <topicref id="signing-detach" href="howto/part04/detached-signing.dita"/> +      <topicref id="signing-clear" href="howto/part04/clear-signing.dita"/> +    </topicref> +    <topicref id="verification" href="howto/part04/verification.dita"/> +  </part> +  <part id="part-5" href="howto/part-5.dita"> +    <topicref id="key-creation" href="howto/part05/key-creation.dita"/> +    <topicref id="primary" href="howto/part05/primary-key.dita"/> +    <topicref id="subkeys" href="howto/part05/subkeys.dita"/> +    <topicref id="user-ids" href="howto/part05/user-ids.dita"> +      <topicref id="add-uid" href="howto/part05/add-uid.dita"/> +      <topicref id="rev-uid" href="howto/part05/rev-uid.dita"/> +    </topicref> +    <topicref id="certify" href="howto/part05/certification.dita"/> +  </part> +  <part id="part-6" href="howto/part-6.dita"> +    <topicref id="groups" href="howto/part06/group-lines.dita"/> +  </part> +</bookmap> diff --git a/lang/python/docs/dita/gpgme-python.ditamap b/lang/python/docs/dita/gpgme-python.ditamap new file mode 100644 index 00000000..5232dca8 --- /dev/null +++ b/lang/python/docs/dita/gpgme-python.ditamap @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "map.dtd"> +<map> +  <title>GPGME Python Bindings</title> +  <mapref id="howto-map" href="gpgme-python-howto.ditamap"/> +</map> diff --git a/lang/python/docs/dita/gpgmePython.xpr b/lang/python/docs/dita/gpgmePython.xpr new file mode 100644 index 00000000..e8756fca --- /dev/null +++ b/lang/python/docs/dita/gpgmePython.xpr @@ -0,0 +1,453 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="20.0"> +    <meta> +        <filters directoryPatterns="" filePatterns="gpgmePython.xpr" positiveFilePatterns="" showHiddenFiles="false"/> +        <options> +            <serialized version="20.0" xml:space="preserve"> +                <serializableOrderedMap> +                    <entry> +                        <String>scenario.associations</String> +                        <scenarioAssociation-array> +                            <scenarioAssociation> +                                <field name="url"> +                                    <String>gpgme-python-howto.ditamap</String> +                                </field> +                                <field name="scenarioIds"> +                                    <list> +                                        <String>gpgme-python-howto (WebHelp Responsive)</String> +                                    </list> +                                </field> +                                <field name="scenarioTypes"> +                                    <list> +                                        <String>DITAMAP</String> +                                    </list> +                                </field> +                            </scenarioAssociation> +                        </scenarioAssociation-array> +                    </entry> +                    <entry> +                        <String>scenarios</String> +                        <scenario-array> +                            <ditaScenario> +                                <field name="useXEP"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="useAntennaHouse"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="baseDir"> +                                    <String>${cfd}</String> +                                </field> +                                <field name="outputDir"> +                                    <String>${cfd}/out/html5</String> +                                </field> +                                <field name="tempDir"> +                                    <String>${cfd}/temp/html5</String> +                                </field> +                                <field name="transtype"> +                                    <String>html5</String> +                                </field> +                                <field name="filters"> +                                    <ditavalFilters> +                                        <field name="useDitavalFilePath"> +                                            <Boolean>false</Boolean> +                                        </field> +                                        <field name="useAppliedConditionSet"> +                                            <Boolean>true</Boolean> +                                        </field> +                                        <field name="appliedConditionSet"> +                                            <null/> +                                        </field> +                                        <field name="ditavalFilePath"> +                                            <null/> +                                        </field> +                                        <field name="simpleFiltersList"> +                                            <list/> +                                        </field> +                                    </ditavalFilters> +                                </field> +                                <field name="addOxygenJars"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="skinCssFile"> +                                    <null/> +                                </field> +                                <field name="lastCustomSkinCssPath"> +                                    <null/> +                                </field> +                                <field name="webhelpResponsiveTemplate"> +                                    <null/> +                                </field> +                                <field name="publishingTemplateDataPO"> +                                    <null/> +                                </field> +                                <field name="additionalAntArgs"> +                                    <String></String> +                                </field> +                                <field name="buildTarget"> +                                    <String></String> +                                </field> +                                <field name="buildFilePath"> +                                    <String></String> +                                </field> +                                <field name="ditaParams"> +                                    <list/> +                                </field> +                                <field name="jvmArgs"> +                                    <String>-Xmx384m</String> +                                </field> +                                <field name="useCustomJavaHome"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="customJavaHomeDir"> +                                    <String></String> +                                </field> +                                <field name="useCustomANTHome"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="customANTHomeDir"> +                                    <String></String> +                                </field> +                                <field name="workingDir"> +                                    <null/> +                                </field> +                                <field name="showConsoleAlways"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="advancedOptionsMap"> +                                    <null/> +                                </field> +                                <field name="name"> +                                    <String>gpgme-python-howto (HTML5)</String> +                                </field> +                                <field name="baseURL"> +                                    <null/> +                                </field> +                                <field name="footerURL"> +                                    <null/> +                                </field> +                                <field name="fOPMethod"> +                                    <null/> +                                </field> +                                <field name="fOProcessorName"> +                                    <null/> +                                </field> +                                <field name="headerURL"> +                                    <null/> +                                </field> +                                <field name="inputXSLURL"> +                                    <null/> +                                </field> +                                <field name="inputXMLURL"> +                                    <null/> +                                </field> +                                <field name="defaultScenario"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="isFOPPerforming"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="type"> +                                    <String>DITAMAP</String> +                                </field> +                                <field name="saveAs"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="openInBrowser"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="outputFile"> +                                    <null/> +                                </field> +                                <field name="outputResource"> +                                    <null/> +                                </field> +                                <field name="openOtherLocationInBrowser"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="locationToOpenInBrowserURL"> +                                    <String></String> +                                </field> +                                <field name="openInEditor"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInHTMLPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInXMLPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInSVGPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInResultSetPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="useXSLTInput"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="xsltParams"> +                                    <list/> +                                </field> +                                <field name="cascadingStylesheets"> +                                    <String-array/> +                                </field> +                                <field name="xslTransformer"> +                                    <String>DITA-OT</String> +                                </field> +                                <field name="extensionURLs"> +                                    <String-array/> +                                </field> +                            </ditaScenario> +                            <ditaScenario> +                                <field name="useXEP"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="useAntennaHouse"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="baseDir"> +                                    <String>${cfd}</String> +                                </field> +                                <field name="outputDir"> +                                    <String>${cfd}/out/webhelp-responsive-${ddt}</String> +                                </field> +                                <field name="tempDir"> +                                    <String>${cfd}/temp/webhelp-responsive-${ddt}</String> +                                </field> +                                <field name="transtype"> +                                    <String>webhelp-responsive</String> +                                </field> +                                <field name="filters"> +                                    <ditavalFilters> +                                        <field name="useDitavalFilePath"> +                                            <Boolean>false</Boolean> +                                        </field> +                                        <field name="useAppliedConditionSet"> +                                            <Boolean>true</Boolean> +                                        </field> +                                        <field name="appliedConditionSet"> +                                            <null/> +                                        </field> +                                        <field name="ditavalFilePath"> +                                            <null/> +                                        </field> +                                        <field name="simpleFiltersList"> +                                            <list/> +                                        </field> +                                    </ditavalFilters> +                                </field> +                                <field name="addOxygenJars"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="skinCssFile"> +                                    <null/> +                                </field> +                                <field name="lastCustomSkinCssPath"> +                                    <null/> +                                </field> +                                <field name="webhelpResponsiveTemplate"> +                                    <null/> +                                </field> +                                <field name="publishingTemplateDataPO"> +                                    <publishingTemplate> +                                        <field name="templateRoot"> +                                            <String>${configured.ditaot.dir}/plugins/com.oxygenxml.webhelp.responsive/templates/oxygen</String> +                                        </field> +                                        <field name="descriptorRelativePath"> +                                            <String>oxygen-tiles.opt</String> +                                        </field> +                                        <field name="isCustomTemplate"> +                                            <Boolean>false</Boolean> +                                        </field> +                                    </publishingTemplate> +                                </field> +                                <field name="additionalAntArgs"> +                                    <String></String> +                                </field> +                                <field name="buildTarget"> +                                    <String></String> +                                </field> +                                <field name="buildFilePath"> +                                    <String></String> +                                </field> +                                <field name="ditaParams"> +                                    <list> +                                        <ditaParameter> +                                            <field name="name"> +                                                <String>force-unique</String> +                                            </field> +                                            <field name="description"> +                                                <null/> +                                            </field> +                                            <field name="value"> +                                                <String>true</String> +                                            </field> +                                            <field name="defaultValue"> +                                                <String>false</String> +                                            </field> +                                            <field name="type"> +                                                <Integer>4</Integer> +                                            </field> +                                            <field name="possibleValues"> +                                                <String-array> +                                                    <String>true</String> +                                                    <String>false</String> +                                                    <null/> +                                                </String-array> +                                            </field> +                                            <field name="possibleValuesDescriptions"> +                                                <String-array> +                                                    <String></String> +                                                    <String></String> +                                                </String-array> +                                            </field> +                                        </ditaParameter> +                                        <ditaParameter> +                                            <field name="name"> +                                                <String>args.xhtml.classattr</String> +                                            </field> +                                            <field name="description"> +                                                <null/> +                                            </field> +                                            <field name="value"> +                                                <String>yes</String> +                                            </field> +                                            <field name="defaultValue"> +                                                <String>yes</String> +                                            </field> +                                            <field name="type"> +                                                <Integer>4</Integer> +                                            </field> +                                            <field name="possibleValues"> +                                                <String-array> +                                                    <String>yes</String> +                                                    <String>no</String> +                                                    <null/> +                                                </String-array> +                                            </field> +                                            <field name="possibleValuesDescriptions"> +                                                <String-array> +                                                    <String></String> +                                                    <String></String> +                                                </String-array> +                                            </field> +                                        </ditaParameter> +                                    </list> +                                </field> +                                <field name="jvmArgs"> +                                    <String>-Xmx384m</String> +                                </field> +                                <field name="useCustomJavaHome"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="customJavaHomeDir"> +                                    <String></String> +                                </field> +                                <field name="useCustomANTHome"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="customANTHomeDir"> +                                    <String></String> +                                </field> +                                <field name="workingDir"> +                                    <null/> +                                </field> +                                <field name="showConsoleAlways"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="advancedOptionsMap"> +                                    <null/> +                                </field> +                                <field name="name"> +                                    <String>gpgme-python-howto (WebHelp Responsive)</String> +                                </field> +                                <field name="baseURL"> +                                    <null/> +                                </field> +                                <field name="footerURL"> +                                    <null/> +                                </field> +                                <field name="fOPMethod"> +                                    <null/> +                                </field> +                                <field name="fOProcessorName"> +                                    <null/> +                                </field> +                                <field name="headerURL"> +                                    <null/> +                                </field> +                                <field name="inputXSLURL"> +                                    <null/> +                                </field> +                                <field name="inputXMLURL"> +                                    <null/> +                                </field> +                                <field name="defaultScenario"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="isFOPPerforming"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="type"> +                                    <String>DITAMAP</String> +                                </field> +                                <field name="saveAs"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="openInBrowser"> +                                    <Boolean>true</Boolean> +                                </field> +                                <field name="outputFile"> +                                    <null/> +                                </field> +                                <field name="outputResource"> +                                    <null/> +                                </field> +                                <field name="openOtherLocationInBrowser"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="locationToOpenInBrowserURL"> +                                    <String></String> +                                </field> +                                <field name="openInEditor"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInHTMLPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInXMLPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInSVGPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="showInResultSetPane"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="useXSLTInput"> +                                    <Boolean>false</Boolean> +                                </field> +                                <field name="xsltParams"> +                                    <list/> +                                </field> +                                <field name="cascadingStylesheets"> +                                    <String-array/> +                                </field> +                                <field name="xslTransformer"> +                                    <String>DITA-OT</String> +                                </field> +                                <field name="extensionURLs"> +                                    <String-array/> +                                </field> +                            </ditaScenario> +                        </scenario-array> +                    </entry> +                </serializableOrderedMap> +            </serialized> +        </options> +    </meta> +    <projectTree name="gpgmePython.xpr"> +        <folder path="."/> +    </projectTree> +</project>
\ No newline at end of file diff --git a/lang/python/docs/dita/howto/part-1.dita b/lang/python/docs/dita/howto/part-1.dita new file mode 100644 index 00000000..12488f8c --- /dev/null +++ b/lang/python/docs/dita/howto/part-1.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_syw_3qx_5db"> +        <title>Introducing the Python Bindings</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part-2.dita b/lang/python/docs/dita/howto/part-2.dita new file mode 100644 index 00000000..6ddd174a --- /dev/null +++ b/lang/python/docs/dita/howto/part-2.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_y52_zwx_5db"> +        <title>Preparation and Setting Up</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part-3.dita b/lang/python/docs/dita/howto/part-3.dita new file mode 100644 index 00000000..37cd3ba9 --- /dev/null +++ b/lang/python/docs/dita/howto/part-3.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_s5w_23y_5db"> +        <title>Working With Keys</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part-4.dita b/lang/python/docs/dita/howto/part-4.dita new file mode 100644 index 00000000..b79a04e8 --- /dev/null +++ b/lang/python/docs/dita/howto/part-4.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_dz3_mpy_5db"> +        <title>The Fun Stuff</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part-5.dita b/lang/python/docs/dita/howto/part-5.dita new file mode 100644 index 00000000..77849a01 --- /dev/null +++ b/lang/python/docs/dita/howto/part-5.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_akd_rwz_5db"> +        <title>Generating Keys</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part-6.dita b/lang/python/docs/dita/howto/part-6.dita new file mode 100644 index 00000000..01b94270 --- /dev/null +++ b/lang/python/docs/dita/howto/part-6.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_bdk_vwz_5db"> +        <title>Miscellaneous Work-arounds</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part01/docs-source.dita b/lang/python/docs/dita/howto/part01/docs-source.dita new file mode 100644 index 00000000..f0a8affa --- /dev/null +++ b/lang/python/docs/dita/howto/part01/docs-source.dita @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_nb3_hrx_5db"> +        <title>Documentation Source Files</title> +        <body> +            <p>Unlike all other documentation in the GnuPG Project, including the initial version of +        this HOWTO, this version was <i>not</i> written in Emacs Org-Mode. Nor was it written in +        LaTeX, Texinfo or even directly in HTML. Instead it was written using the Darwin Information +        Typing Architecture (DITA) XML.</p> +      <p>This was done for two main reasons:</p> +      <p> +        <ol id="ol_k3b_wrx_5db"> +          <li>A bug in either Org-Mode or Babel prevented the more complex examples included in the +            HOWTO from displaying correctly.</li> +          <li>To demonstrate some of the advantages of DITA XML over existing documentation +            productionsoftware used in the project (particularly Texinfo and LaTeX).</li> +        </ol> +      </p> +      <p>The XML format definitely supports displaying all the more complex Python code correctly, +        as well as being designed to produce standards compliant print and HTML output.  Whereas +        currently the existing tools utilised by the GnuPG Project can't display the example code in +        a way which would actually pass the project's own git commit ruleset.</p> +      <p> </p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part01/examples.dita b/lang/python/docs/dita/howto/part01/examples.dita new file mode 100644 index 00000000..afa66197 --- /dev/null +++ b/lang/python/docs/dita/howto/part01/examples.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_bv2_qqx_5db"> +        <title>Examples</title> +        <body> +            <p>All of the examples found in this document can be found as Python 3 scripts in the +          <filepath>lang/python/examples/howto</filepath> directory.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part01/introduction.dita b/lang/python/docs/dita/howto/part01/introduction.dita new file mode 100644 index 00000000..7b18eb89 --- /dev/null +++ b/lang/python/docs/dita/howto/part01/introduction.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_ucm_3mx_5db"> +        <title>Introduction</title> +        <body> +      <p>This document provides basic instruction in how to use the GPGME Python bindings to +        programmatically leverage the GPGME library.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part01/python2-vs-python3.dita b/lang/python/docs/dita/howto/part01/python2-vs-python3.dita new file mode 100644 index 00000000..97662764 --- /dev/null +++ b/lang/python/docs/dita/howto/part01/python2-vs-python3.dita @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_j1r_qmx_5db"> +        <title>Python 2 vs. Python 3</title> +        <body> +            <p>Though the GPGME Python bindings themselves provide support for both Python 2 and 3, +        the focus is unequivocally on Python 3 and specifically from Python 3.4 and above. As a +        consequence all the examples and instructions in this guide use Python 3 code.</p> +      <p>Much of it will work with Python 2, but much of it also deals with Python 3 byte literals, +        particularly when reading and writing data. Developers concentrating on Python 2.7, and +        possibly even 2.6, will need to make the appropriate modifications to support the older +        string and unicode types as opposed to bytes.</p> +      <p>There are multiple reasons for concentrating on Python 3; some of which relate to the +        immediate integration of these bindings, some of which relate to longer term plans for both +        GPGME and the python bindings and some of which relate to the impending EOL period for +        Python 2.7. Essentially, though, there is little value in tying the bindings to a version of +        the language which is a dead end and the advantages offered by Python 3 over Python 2 make +        handling the data types with which GPGME deals considerably easier.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/c-api.dita b/lang/python/docs/dita/howto/part02/c-api.dita new file mode 100644 index 00000000..67736ab2 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/c-api.dita @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_jrb_pxx_5db"> +        <title>A C API</title> +        <body> +            <p>Unlike many modern APIs with which programmers will be more familiar with these days, +        the GPGME API is a C API. The API is intended for use by C coders who would be able to +        access its features by including the =gpgme.h= header file with their own C source code and +        then access its functions just as they would any other C headers.</p> +      <p>This is a very effective method of gaining complete access to the API and in the most +        efficient manner possible. It does, however, have the drawback that it cannot be directly +        used by other languages without some means of providing an interface to those languages. +        This is where the need for bindings in various languages stems.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/context.dita b/lang/python/docs/dita/howto/part02/context.dita new file mode 100644 index 00000000..4b137af8 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/context.dita @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_bpb_why_5db"> +        <title>Context</title> +        <body> +            <p>One of the reasons which prevents this API from being RESTful is that most operations +        require more than one instruction to the API to perform the task. Sure, there are certain +        functions which can be performed simultaneously, particularly if the result known or +        strongly anticipated (e.g. selecting and encrypting to a key known to be in the public +        keybox).</p> +      <p>There are many more, however, which cannot be manipulated so readily: they must be +        performed in a specific sequence and the result of one operation has a direct bearing on the +        outcome of subsequent operations. Not merely by generating an error either.</p> +      <p>When dealing with this type of persistent state on the web, full of both the RESTful and +        REST-like, it's most commonly referred to as a session. In GPGME, however, it is called a +        context and every operation type has one.</p> +      <p/> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/daesh.dita b/lang/python/docs/dita/howto/part02/daesh.dita new file mode 100644 index 00000000..0d02f45e --- /dev/null +++ b/lang/python/docs/dita/howto/part02/daesh.dita @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_ob2_yzx_5db"> +        <title>The gnupg package created and maintained by Isis Lovecruft</title> +        <body> +            <p>In 2015 Isis Lovecruft from the Tor Project forked and then re-implemented the +        python-gnupg package as just gnupg. This new package also relied on +          <codeph>subprocess</codeph> to call the <cmdname>gpg</cmdname> or <cmdname>gpg2</cmdname> +        binaries, but did so somewhat more securely.</p> +      <p>The naming and version numbering selected for this package, however, resulted in conflicts +        with the original python-gnupg and since its functions were called in a different manner to +        python-gnupg, the release of this package also resulted in a great deal of consternation +        when people installed what they thought was an upgrade that subsequently broke the code +        relying on it.</p> +      <p>The gnupg package is available under the GNU General Public License version 3.0 (or any +        later version).</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/differences-to-others.dita b/lang/python/docs/dita/howto/part02/differences-to-others.dita new file mode 100644 index 00000000..dd3521f2 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/differences-to-others.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_ecj_yyx_5db"> +        <title>Difference between the Python bindings and other GnuPG Python packages</title> +        <body> +            <p>There have been numerous attempts to add GnuPG support to Python over the years. Some +        of the most well known are listed here, along with what differentiates them.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/fundamentals.dita b/lang/python/docs/dita/howto/part02/fundamentals.dita new file mode 100644 index 00000000..a81faf50 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/fundamentals.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_xsp_ygy_5db"> +        <title>Fundamentals</title> +        <body> +            <p>Before we can get to the fun stuff, there are a few matters regarding GPGME's design +        which hold true whether you're dealing with the C code directly or these Python +        bindings.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/gpgme-concepts.dita b/lang/python/docs/dita/howto/part02/gpgme-concepts.dita new file mode 100644 index 00000000..436111bf --- /dev/null +++ b/lang/python/docs/dita/howto/part02/gpgme-concepts.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_zm1_lxx_5db"> +    <title>GPGME Concepts</title> +    <body> +      <p/> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/installation.dita b/lang/python/docs/dita/howto/part02/installation.dita new file mode 100644 index 00000000..4e35dc99 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/installation.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_isl_32y_5db"> +        <title>GPGME Python bindings installation</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/installing.dita b/lang/python/docs/dita/howto/part02/installing.dita new file mode 100644 index 00000000..91a0cf49 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/installing.dita @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_ivh_zfy_5db"> +    <title>Installing</title> +    <body> +      <p>Installing the Python bindings is effectively achieved by compiling and installing GPGME +        itself.</p> +      <p>Once SWIG is installed with Python and all the dependencies for GPGME are installed you +        only need to confirm that the version(s) of Python you want the bindings installed for are +        in your <codeph>$PATH</codeph>.</p> +      <p>By default GPGME will attempt to install the bindings for the most recent or highest +        version number of Python 2 and Python 3 it detects in <codeph>$PATH</codeph>. It +        specifically checks for the <cmdname>python</cmdname> and <cmdname>python3</cmdname> +        executables first and then checks for specific version numbers.</p> +      <p>For Python 2 it checks for these executables in this order: <cmdname>python</cmdname>, +          <cmdname>python2</cmdname> and <cmdname>python2.7</cmdname>.</p> +      <p>For Python 3 it checks for these executables in this order: <cmdname>python3</cmdname>, +          <cmdname>python3.6</cmdname>, <cmdname>python3.5</cmdname> and +          <cmdname>python3.4</cmdname>.</p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/no-pypi.dita b/lang/python/docs/dita/howto/part02/no-pypi.dita new file mode 100644 index 00000000..8f2b6013 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/no-pypi.dita @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_mss_p2y_5db"> +        <title>No PyPI</title> +        <body> +            <p>Most third-party Python packages and modules are available and distributed through +        the Python Package Installer, known as PyPI.</p> +      <p>Due to the nature of what these bindings are and how they work, it is infeasible to install +        the GPGME Python bindings in the same way.</p> +      <p>This is because the bindings use SWIG to dynamically generate C bindings against +          <codeph>gpgme.h</codeph> and <codeph>gpgme.h</codeph> is generated from +          <codeph>gpgme.h.in</codeph> at compile time when GPGME is built from source. Thus to +        include a package in PyPI which actually built correctly would require either statically +        built libraries for every architecture bundled with it or a full implementation of C for +        each architecture.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/no-rest.dita b/lang/python/docs/dita/howto/part02/no-rest.dita new file mode 100644 index 00000000..82cf8e80 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/no-rest.dita @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_wmg_2hy_5db"> +        <title>No REST</title> +        <body> +            <p>The first part of which is or will be fairly blatantly obvious upon viewing the first +        example, but it's worth reiterating anyway. That being that this API is <b><i>not</i></b> a +        REST API. Nor indeed could it ever be one.</p> +      <p>Most, if not all, Python programmers (and not just Python programmers) know how easy it is +        to work with a RESTful API. In fact they've become so popular that many other APIs attempt +        to emulate REST-like behaviour as much as they are able. Right down to the use of JSON +        formatted output to facilitate the use of their API without having to retrain +        developers.</p> +      <p>This API does not do that. It would not be able to do that and also provide access to the +        entire C API on which it's built. It does, however, provide a very pythonic interface on top +        of the direct bindings and it's this pythonic layer with which this HOWTO deals with.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/pyme.dita b/lang/python/docs/dita/howto/part02/pyme.dita new file mode 100644 index 00000000..3ced2dc5 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/pyme.dita @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_oy4_zcy_5db"> +    <title>The PyME package maintained by Martin Albrecht</title> +    <body> +      <p>This package is the origin of these bindings, though they are somewhat different now. For +        details of when and how the PyME package was folded back into GPGME itself see the +          <cite>Short History</cite> document<fn><codeph>Short_History.org</codeph> and/or +            <codeph>Short_History.html</codeph>.</fn> in the Python bindings <codeph>docs/</codeph> +          directory.<fn>The <filepath>lang/python/docs/</filepath> directory in the GPGME +          source.</fn></p> +      <p>The PyME package was first released in 2002 and was also the first attempt to implement a +        low level binding to GPGME. In doing so it provided access to considerably more +        functionality than either the <codeph>python-gnupg</codeph> or <codeph>gnupg</codeph> +        packages.</p> +      <p>The PyME package is only available for Python 2.6 and 2.7.</p> +      <p>Porting the PyME package to Python 3.4 in 2015 is what resulted in it being folded into the +        GPGME project and the current bindings are the end result of that effort.</p> +      <p>The PyME package is available under the same dual licensing as GPGME itself: the GNU +        General Public License version 2.0 (or any later version) and the GNU Lesser General Public +        License version 2.1 (or any later version).</p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/python-bindings.dita b/lang/python/docs/dita/howto/part02/python-bindings.dita new file mode 100644 index 00000000..6590fee4 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/python-bindings.dita @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_g4z_wxx_5db"> +        <title>Python Bindings</title> +        <body> +            <p>The Python bindings for GPGME provide a higher level means of accessing the complete +        feature set of GPGME itself. It also provides a more pythonic means of calling these API +        functions.</p> +      <p>The bindings are generated dynamically with SWIG and the copy of <codeph>gpgme.h</codeph> +        generated when GPGME is compiled.</p> +      <p>This means that a version of the Python bindings is fundamentally tied to the exact same +        version of GPGME used to generate that copy of <codeph>gpgme.h</codeph>.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/python-gnupg.dita b/lang/python/docs/dita/howto/part02/python-gnupg.dita new file mode 100644 index 00000000..f083a7a6 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/python-gnupg.dita @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_llw_kzx_5db"> +        <title>The python-gnupg package maintained by Vinay Sajip</title> +        <body> +            <p>This is arguably the most popular means of integrating GPG with Python. The package +        utilises the <codeph>subprocess</codeph> module to implement wrappers for the +          <cmdname>gpg</cmdname> and <cmdname>gpg2</cmdname> executables normally invoked on the command +        line (<cmdname>gpg.exe</cmdname> and <cmdname>gpg2.exe</cmdname> on Windows).</p> +      <p>The popularity of this package stemmed from its ease of use and capability in providing the +        most commonly required features.</p> +      <p>Unfortunately it has been beset by a number of security issues in the past; most of which +        stemmed from using unsafe methods of accessing the command line via the +          <codeph>subprocess</codeph> calls. While some effort has been made over the last two to +        three years (as of 2018) to mitigate this, particularly by no longer providing shell access +        through those subprocess calls, the wrapper is still somewhat limited in the scope of its +        GnuPG features coverage.</p> +      <p>The python-gnupg package is available under the MIT license.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part02/requirements.dita b/lang/python/docs/dita/howto/part02/requirements.dita new file mode 100644 index 00000000..584219d5 --- /dev/null +++ b/lang/python/docs/dita/howto/part02/requirements.dita @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_zqn_nfy_5db"> +        <title>Requirements</title> +        <body> +            <p>The GPGME Python bindings only have three requirements:</p> +      <p> +        <ol id="ol_rbw_qfy_5db"> +          <li>A suitable version of Python 2 or Python 3. With Python 2 that means Python 2.7 and +            with Python 3 that means Python 3.4 or higher.</li> +          <li>SWIG.</li> +          <li>GPGME itself. Which also means that all of GPGME's dependencies must be installed +            too.</li> +        </ol> +      </p> +      <p/> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part03/get-key.dita b/lang/python/docs/dita/howto/part03/get-key.dita new file mode 100644 index 00000000..1e3309c2 --- /dev/null +++ b/lang/python/docs/dita/howto/part03/get-key.dita @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_j3h_5my_5db"> +        <title>Get Key</title> +        <body> +            <p>An alternative method of getting a single key via its fingerprint is available +        directly within a Context with Context().get_key. This is the preferred method of selecting +        a key in order to modify it, sign or certify it and for obtaining relevant data about a +        single key as a part of other functions; when verifying a signature made by that key, for +        instance.</p> +      <p>By default this method will select public keys, but it can select secret keys as well.</p> +      <p>This first example demonstrates selecting the current key of Werner Koch, which is due to +        expire at the end of 2018:</p> +      <p> +        <codeblock id="getkey-1" outputclass="language-python">import gpg + +fingerprint = "80615870F5BAD690333686D0F2AD85AC1E42B367" +key = gpg.Context().get_key(fingerprint) +        </codeblock> +      </p> +      <p>Whereas this example demonstrates selecting the author's current key with the secret key +        word argument set to True:</p> +      <p> +        <codeblock id="getkey-2" outputclass="language-python">import gpg + +fingerprint = "DB4724E6FA4286C92B4E55C4321E4E2373590E5D" +key = gpg.Context().get_key(fingerprint, secret=True) +        </codeblock> +      </p> +      <p>It is, of course, quite possible to select expired, disabled and revoked keys with this +        function, but only to effectively display information about those keys.</p> +      <p>It is also possible to use both unicode or string literals and byte literals with the +        fingerprint when getting a key in this way.</p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part03/key-counting.dita b/lang/python/docs/dita/howto/part03/key-counting.dita new file mode 100644 index 00000000..b26fbd2d --- /dev/null +++ b/lang/python/docs/dita/howto/part03/key-counting.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_jgw_wly_5db"> +    <title>Counting Keys</title> +    <body> +      <p>Counting the number of keys in your public keybox (<filepath>pubring.kbx</filepath>), the +        format which has superseded the old keyring format (<filepath>pubring.gpg</filepath> and +          <filepath>secring.gpg</filepath>), or the number of secret keys is a very simple task.</p> +      <p> +        <codeblock id="kc" outputclass="language-python">import gpg + +c = gpg.Context() +seckeys = c.keylist(pattern=None, secret=True) +pubkeys = c.keylist(pattern=None, secret=False) + +seclist = list(seckeys) +secnum = len(seclist) + +publist = list(pubkeys) +pubnum = len(publist) + +print(""" +Number of secret keys:  {0} +Number of public keys:  {1} +""".format(secnum, pubnum)) +</codeblock> +      </p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part03/key-selection.dita b/lang/python/docs/dita/howto/part03/key-selection.dita new file mode 100644 index 00000000..3e51a4da --- /dev/null +++ b/lang/python/docs/dita/howto/part03/key-selection.dita @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_flg_p3y_5db"> +    <title>Key Selection</title> +    <body> +      <p>Selecting keys to encrypt to or to sign with will be a common occurrence when working with +        GPGMe and the means available for doing so are quite simple.</p> +      <p>They do depend on utilising a Context; however once the data is recorded in another +        variable, that Context does not need to be the same one which subsequent operations are +        performed.</p> +      <p>The easiest way to select a specific key is by searching for that key's key ID or +        fingerprint, preferably the full fingerprint without any spaces in it. A long key ID will +        probably be okay, but is not advised and short key IDs are already a problem with some being +        generated to match specific patterns. It does not matter whether the pattern is upper or +        lower case.</p> +      <p>So this is the best method:</p> +      <p> +        <codeblock id="keysel-01" outputclass="language-python">import gpg + +k = gpg.Context().keylist(pattern="258E88DCBD3CD44D8E7AB43F6ECB6AF0DEADBEEF") +keys = list(k) +        </codeblock> +      </p> +      <p>This is passable and very likely to be common:</p> +      <p> +        <codeblock id="keysel-02" outputclass="language-python">import gpg + +k = gpg.Context().keylist(pattern="0x6ECB6AF0DEADBEEF") +keys = list(k) +        </codeblock> +      </p> +      <p>And this is a really bad idea:</p> +      <p> +        <codeblock id="keysel-03" outputclass="language-python">import gpg + +k = gpg.Context().keylist(pattern="0xDEADBEEF") +keys = list(k) +        </codeblock> +      </p> +      <p>Alternatively it may be that the intention is to create a list of keys which all match a +        particular search string. For instance all the addresses at a particular domain, like +        this:</p> +      <p> +        <codeblock id="keysel-04" outputclass="language-python">import gpg + +ncsc = gpg.Context().keylist(pattern="ncsc.mil") +nsa = list(ncsc) +        </codeblock> +      </p> +    </body> +  </topic> +</dita> 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> diff --git a/lang/python/docs/dita/howto/part05/add-uid.dita b/lang/python/docs/dita/howto/part05/add-uid.dita new file mode 100644 index 00000000..4265d421 --- /dev/null +++ b/lang/python/docs/dita/howto/part05/add-uid.dita @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_e4q_jyz_5db"> +    <title>Adding a User ID</title> +    <body> +      <p>By comparison to creating primary keys and subkeys, adding a new user ID to an existing key +        is much simpler. The method used to do this is <codeph>key_add_uid</codeph> and the only +        arguments it takes are for the <codeph>key</codeph> and the new <codeph>uid</codeph>.</p> +      <p> +        <codeblock id="adduid-1" outputclass="language-python">import gpg + +c = gpg.Context() +c.home_dir = "~/.gnupg-dm" + +dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA" +key = c.get_key(dmfpr, secret=True) +uid = "Danger Mouse <[email protected]>" + +c.key_add_uid(key, uid) +</codeblock> +      </p> +      <p>Unsurprisingly the result of this is:</p> +      <p> +        <codeblock id="adduid-2" outputclass="language-bourne">bash-4.4$ gpg --homedir ~/.gnupg-dm -K +~/.gnupg-dm/pubring.kbx +---------------------- +sec   rsa3072 2018-03-15 [SC] [expires: 2019-03-15] +      177B7C25DB99745EE2EE13ED026D2F19E99E63AA +uid           [ultimate] Danger Mouse <[email protected]> +uid           [ultimate] Danger Mouse <[email protected]> +ssb   rsa3072 2018-03-15 [E] [expires: 2018-09-13] + +bash-4.4$ +</codeblock> +      </p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part05/certification.dita b/lang/python/docs/dita/howto/part05/certification.dita new file mode 100644 index 00000000..072c774e --- /dev/null +++ b/lang/python/docs/dita/howto/part05/certification.dita @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_p15_1yz_5db"> +    <title>Key Certification</title> +    <body> +      <p>Since key certification is more frequently referred to as key signing, the method used to +        perform this function is <codeph>key_sign</codeph>.</p> +      <p>The <codeph>key_sign</codeph> method takes four arguments: <codeph>key</codeph>, +          <codeph>uids</codeph>, <codeph>expires_in</codeph> and <codeph>local</codeph>. The default +        value of <codeph>uids</codeph> is <codeph>None</codeph> and which results in all user IDs +        being selected. The default value of both <codeph>expires_in</codeph> and +          <codeph>local</codeph> is <codeph>False</codeph>; which results in the signature never +        expiring and being able to be exported.</p> +      <p>The <codeph>key</codeph> is the key being signed rather than the key doing the signing. To +        change the key doing the signing refer to the signing key selection above for signing +        messages and files.</p> +      <p>If the <codeph>uids</codeph> value is not <codeph>None</codeph> then it must either be a +        string to match a single user ID or a list of strings to match multiple user IDs. In this +        case the matching of those strings must be precise and it is case sensitive.</p> +      <p>To sign Danger Mouse's key for just the initial user ID with a signature which will last a +        little over a month, do this:</p> +      <p> +        <codeblock id="cert-1" outputclass="language-python">import gpg + +c = gpg.Context() +uid = "Danger Mouse <[email protected]>" + +dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA" +key = c.get_key(dmfpr, secret=True) +c.key_sign(key, uidsuid, expires_in=2764800) +</codeblock> +      </p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part05/key-creation.dita b/lang/python/docs/dita/howto/part05/key-creation.dita new file mode 100644 index 00000000..6478bf95 --- /dev/null +++ b/lang/python/docs/dita/howto/part05/key-creation.dita @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_c4z_syd_vdb"> +    <title>Creating Keys and Subkeys</title> +    <body> +      <p>The one thing, aside from GnuPG itself, that GPGME depends on, of course, is the keys +        themselves. So it is necessary to be able to generate them and modify them by adding +        subkeys, revoking or disabling them, sometimes deleting them and doing the same for user +        IDs.</p> +      <p>In the following examples a key will be created for the world's greatest secret agent, +        Danger Mouse. Since Danger Mouse is a secret agent he needs to be able to protect +        information to <tt>SECRET</tt> level clearance, so his keys will be 3072-bit keys.</p> +      <p>The pre-configured <filepath>gpg.conf</filepath> file which sets cipher, digest and other +        preferences contains the following configuration parameters:</p> +      <p> +        <codeblock id="gpg-config">expert +allow-freeform-uid +allow-secret-key-import +trust-model tofu+pgp +tofu-default-policy unknown +enable-large-rsa +enable-dsa2 +cert-digest-algo SHA512 +default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed +personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES +personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 +personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed +</codeblock> +      </p> +      <p/> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part05/primary-key.dita b/lang/python/docs/dita/howto/part05/primary-key.dita new file mode 100644 index 00000000..5401dc9f --- /dev/null +++ b/lang/python/docs/dita/howto/part05/primary-key.dita @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_nfy_byz_5db"> +    <title>Primary Key Creation</title> +    <body> +      <p>Generating a primary key uses the <codeph>create_key</codeph> method in a Context. It +        contains multiple arguments and keyword arguments, including: <codeph>userid</codeph>, +          <codeph>algorithm</codeph>, <codeph>expires_in</codeph>, <codeph>expires</codeph>, +          <codeph>sign</codeph>, <codeph>encrypt</codeph>, <codeph>certify</codeph>, +          <codeph>authenticate</codeph>, <codeph>passphrase</codeph> and <codeph>force</codeph>. The +        defaults for all of those except <codeph>userid</codeph>, <codeph>algorithm</codeph>, +          <codeph>expires_in</codeph>, <codeph>expires</codeph> and <codeph>passphrase</codeph> is +          <codeph>False</codeph>. The defaults for <codeph>algorithm</codeph> and +          <codeph>passphrase</codeph> is <codeph>None</codeph>. The default for +          <codeph>expires_in</codeph> is <codeph>0</codeph>. The default for +          <codeph>expires</codeph> is <codeph>True</codeph>. There is no default for +          <codeph>userid</codeph>.</p> +      <p>If <codeph>passphrase</codeph> is left as <codeph>None</codeph> then the key will not be +        generated with a passphrase, if <codeph>passphrase</codeph> is set to a string then that +        will be the passphrase and if <codeph>passphrase</codeph> is set to <codeph>True</codeph> +        then gpg-agent will launch pinentry to prompt for a passphrase. For the sake of convenience, +        these examples will keep passphrase set to <codeph>None</codeph>.</p> +      <p> +        <codeblock id="keygen-1" outputclass="language-python">import gpg + +c = gpg.Context() + +c.home_dir = "~/.gnupg-dm" +userid = "Danger Mouse <[email protected]>" + +dmkey = c.create_key(userid, algorithm="rsa3072", expires_in=31536000, +		       sign=True, certify=True) +</codeblock> +      </p> +      <p>One thing to note here is the use of setting the <codeph>c.home_dir</codeph> parameter. +        This enables generating the key or keys in a different location. In this case to keep the +        new key data created for this example in a separate location rather than adding it to +        existing and active key store data. As with the default directory, +          <filepath>~/.gnupg</filepath>, any temporary or separate directory needs the permissions +        set to only permit access by the directory owner. On posix systems this means setting the +        directory permissions to <codeph>700</codeph>.</p> +      <p>The <cmdname>temp-homedir-config.py</cmdname> script in the HOWTO examples directory will +        create an alternative homedir with these configuration options already set and the correct +        directory and file permissions.</p> +      <p>The successful generation of the key can be confirmed via the returned +          <codeph>GenkeyResult</codeph> object, which includes the following data:</p> +      <p> +        <codeblock id="keygen-2" outputclass="language-python">print(""" +Fingerprint:  {0} +Primary Key:  {1} + Public Key:  {2} + Secret Key:  {3} +    Sub Key:  {4} +   User IDs:  {5} +""".format(dmkey.fpr, dmkey.primary, dmkey.pubkey, dmkey.seckey, dmkey.sub, +	    dmkey.uid)) +</codeblock> +      </p> +      <p>Alternatively the information can be confirmed using the command line program:</p> +      <p> +        <codeblock id="keygen-3" outputclass="language-bourne">bash-4.4$ gpg --homedir ~/.gnupg-dm -K +~/.gnupg-dm/pubring.kbx +---------------------- +sec   rsa3072 2018-03-15 [SC] [expires: 2019-03-15] +      177B7C25DB99745EE2EE13ED026D2F19E99E63AA +uid           [ultimate] Danger Mouse <[email protected]> + +bash-4.4$ +</codeblock> +      </p> +      <p>As with generating keys manually, to preconfigure expanded preferences for the cipher, +        digest and compression algorithms, the <filepath>gpg.conf</filepath> file must contain those +        details in the home directory in which the new key is being generated. I used a cut down +        version of my own <filepath>gpg.conf</filepath> file in order to be able to generate +        this:</p> +      <p> +        <codeblock id="keygen-4" outputclass="language-bourne">bash-4.4$ gpg --homedir ~/.gnupg-dm --edit-key 177B7C25DB99745EE2EE13ED026D2F19E99E63AA showpref quit +Secret key is available. + +sec  rsa3072/026D2F19E99E63AA +     created: 2018-03-15  expires: 2019-03-15  usage: SC +     trust: ultimate      validity: ultimate +[ultimate] (1). Danger Mouse <[email protected]> + +[ultimate] (1). Danger Mouse <[email protected]> +     Cipher: TWOFISH, CAMELLIA256, AES256, CAMELLIA192, AES192, CAMELLIA128, AES, BLOWFISH, IDEA, CAST5, 3DES +     Digest: SHA512, SHA384, SHA256, SHA224, RIPEMD160, SHA1 +     Compression: ZLIB, BZIP2, ZIP, Uncompressed +     Features: MDC, Keyserver no-modify + +bash-4.4$ +</codeblock> +      </p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part05/rev-uid.dita b/lang/python/docs/dita/howto/part05/rev-uid.dita new file mode 100644 index 00000000..18d6efbd --- /dev/null +++ b/lang/python/docs/dita/howto/part05/rev-uid.dita @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +  <topic id="topic_fbb_lyz_5db"> +    <title>Revoking a User ID</title> +    <body> +      <p>Revoking a user ID is a fairly similar process, except that it uses the +          <codeph>key_revoke_uid</codeph> method.</p> +      <p> +        <codeblock id="revuid" outputclass="language-python">import gpg + +c = gpg.Context() +c.home_dir = "~/.gnupg-dm" + +dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA" +key = c.get_key(dmfpr, secret=True) +uid = "Danger Mouse <[email protected]>" + +c.key_revoke_uid(key, uid) +</codeblock> +      </p> +    </body> +  </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part05/subkeys.dita b/lang/python/docs/dita/howto/part05/subkeys.dita new file mode 100644 index 00000000..81491979 --- /dev/null +++ b/lang/python/docs/dita/howto/part05/subkeys.dita @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_cb4_fyz_5db"> +        <title>Subkey Creation</title> +        <body> +            <p>Adding subkeys to a primary key is fairly similar to creating the primary key with +        the <codeph>create_subkey</codeph> method. Most of the arguments are the same, but not quite +        all. Instead of the <codeph>userid</codeph> argument there is now a <codeph>key</codeph> +        argument for selecting which primary key to add the subkey to.</p> +      <p>In the following example an encryption subkey will be added to the primary key. Since +        Danger Mouse is a security conscious secret agent, this subkey will only be valid for about +        six months, half the length of the primary key.</p> +      <p> +        <codeblock id="subkey-1" outputclass="language-python">import gpg + +c = gpg.Context() +c.home_dir = "~/.gnupg-dm" + +key = c.get_key(dmkey.fpr, secret=True) +dmsub = c.create_subkey(key, algorithm="rsa3072", expires_in=15768000, +			    encrypt=True) +</codeblock> +      </p> +      <p>As with the primary key, the results here can be checked with:</p> +      <p> +        <codeblock id="subkey-2" outputclass="language-python">print(""" +Fingerprint:  {0} +Primary Key:  {1} + Public Key:  {2} + Secret Key:  {3} +    Sub Key:  {4} +   User IDs:  {5} +""".format(dmsub.fpr, dmsub.primary, dmsub.pubkey, dmsub.seckey, dmsub.sub, +	    dmsub.uid)) +</codeblock> +      </p> +      <p>As well as on the command line with:</p> +      <p> +        <codeblock id="subkey-3" outputclass="language-bourne">bash-4.4$ gpg --homedir ~/.gnupg-dm -K +~/.gnupg-dm/pubring.kbx +---------------------- +sec   rsa3072 2018-03-15 [SC] [expires: 2019-03-15] +      177B7C25DB99745EE2EE13ED026D2F19E99E63AA +uid           [ultimate] Danger Mouse <[email protected]> +ssb   rsa3072 2018-03-15 [E] [expires: 2018-09-13] + +bash-4.4$ +</codeblock> +      </p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part05/user-ids.dita b/lang/python/docs/dita/howto/part05/user-ids.dita new file mode 100644 index 00000000..9308c0a6 --- /dev/null +++ b/lang/python/docs/dita/howto/part05/user-ids.dita @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_d1j_hyz_5db"> +        <title>User IDs</title> +        <body> +            <p></p> +        </body> +    </topic> +</dita> diff --git a/lang/python/docs/dita/howto/part06/group-lines.dita b/lang/python/docs/dita/howto/part06/group-lines.dita new file mode 100644 index 00000000..5a63d150 --- /dev/null +++ b/lang/python/docs/dita/howto/part06/group-lines.dita @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd"> +<dita> +    <topic id="topic_gbm_bxz_5db"> +        <title>Group Lines</title> +        <body> +            <p>There is not yet an easy way to access groups configured in the +          <filepath>gpg.conf</filepath> file from within GPGME. As a consequence these central +        groupings of keys cannot be shared amongst multiple programs, such as MUAs readily.</p> +      <p>The following code, however, provides a work-around for obtaining this information in +        Python.</p> +      <p> +        <codeblock id="groups-1" outputclass="language-python">import subprocess + +lines = subprocess.getoutput("gpgconf --list-options gpg").splitlines() + +for i in range(len(lines)): +    if lines[i].startswith("group") is True: +        line = lines[i] +    else: +        pass + +groups = line.split(":")[-1].replace('"', '').split(',') + +group_lines = groups +for i in range(len(group_lines)): +    group_lines[i] = group_lines[i].split("=") + +group_lists = group_lines +for i in range(len(group_lists)): +    group_lists[i][1] = group_lists[i][1].split() +</codeblock> +      </p> +      <p>The result of that code is that <codeph>group_lines</codeph> is a list of lists where +          <codeph>group_lines[i][0]</codeph> is the name of the group and +          <codeph>group_lines[i][1]</codeph> is the key IDs of the group as a string.</p> +      <p>The <codeph>group_lists</codeph> result is very similar in that it is a list of lists. The +        first part, <codeph>group_lists[i][0]</codeph> matches <codeph>group_lines[i][0]</codeph> as +        the name of the group, but <codeph>group_lists[i][1]</codeph> is the key IDs of the group as +        a string.</p> +      <p>To use this code as a module use:</p> +      <p> +        <codeblock id="groups-2" outputclass="language-python">from groups import group_lists</codeblock> +      </p> +        </body> +    </topic> +</dita> | 
