paul@11 | 1 | Introduction
|
paul@11 | 2 | ------------
|
paul@11 | 3 |
|
paul@11 | 4 | MoinMessage provides a library for creating, signing, encrypting, decrypting,
|
paul@12 | 5 | and verifying PGP/GPG content in Python along with mechanisms for updating
|
paul@11 | 6 | MoinMoin Wiki instances with such content such that contributors can be
|
paul@11 | 7 | identified from their PGP signatures and such details used to authenticate
|
paul@11 | 8 | their contributions.
|
paul@11 | 9 |
|
paul@11 | 10 | Configuring GPG for a Wiki
|
paul@11 | 11 | --------------------------
|
paul@11 | 12 |
|
paul@37 | 13 | Initialise a homedir for GPG and configure it using filesystem ACL (access
|
paul@37 | 14 | control list) properties:
|
paul@11 | 15 |
|
paul@12 | 16 | ./scripts/init_wiki_keyring.sh WIKI WEBUSER
|
paul@12 | 17 |
|
paul@12 | 18 | Here, WIKI should be replaced by the top-level Wiki instance directory, and
|
paul@12 | 19 | WEBUSER should be the name of the user under which the Web server operates.
|
paul@12 | 20 |
|
paul@12 | 21 | Note that this script may need re-running after the homedir has been changed
|
paul@12 | 22 | by gpg operations as gpg likes to remove permissions from various files.
|
paul@11 | 23 |
|
paul@11 | 24 | To be in any way useful, signing keys must be made available within this
|
paul@11 | 25 | homedir so that incoming messages can have their senders verified.
|
paul@11 | 26 |
|
paul@11 | 27 | To see the keys available to you in your own environment:
|
paul@11 | 28 |
|
paul@11 | 29 | gpg --list-keys --with-fingerprint
|
paul@11 | 30 |
|
paul@11 | 31 | The full fingerprints are used when defining a user mapping in the Wiki, and
|
paul@11 | 32 | the --with-fingerprint option is used to show them. Otherwise, only the last
|
paul@11 | 33 | eight characters of the fingerprints are shown.
|
paul@11 | 34 |
|
paul@11 | 35 | Export the public key used when signing messages from your own environment:
|
paul@11 | 36 |
|
paul@11 | 37 | gpg --armor --output 1C1AAF83.asc --export 1C1AAF83
|
paul@11 | 38 |
|
paul@11 | 39 | Import the key into the Wiki's GPG homedir:
|
paul@11 | 40 |
|
paul@11 | 41 | gpg --homedir wiki/gnupg --import 1C1AAF83.asc
|
paul@11 | 42 |
|
paul@11 | 43 | For the Wiki to receive encrypted data, a key for the Wiki must be created:
|
paul@11 | 44 |
|
paul@11 | 45 | gpg --homedir wiki/gnupg --gen-key
|
paul@11 | 46 |
|
paul@12 | 47 | For the Wiki environment to be able to use the key, password access must be
|
paul@12 | 48 | disabled. This can be done by either not specifying a password or by removing
|
paul@12 | 49 | it later using the --edit-key option.
|
paul@12 | 50 |
|
paul@11 | 51 | Export the Wiki's key for encrypting messages sent to the Wiki:
|
paul@11 | 52 |
|
paul@11 | 53 | gpg --homedir wiki/gnupg --armor --output 0891463A.asc --export 0891463A
|
paul@11 | 54 |
|
paul@11 | 55 | This exported key can now be imported into your own environment:
|
paul@11 | 56 |
|
paul@11 | 57 | gpg --import 0891463A.asc
|
paul@11 | 58 |
|
paul@11 | 59 | Configuring the Wiki
|
paul@11 | 60 | --------------------
|
paul@11 | 61 |
|
paul@11 | 62 | In the Wiki configuration, define the following settings:
|
paul@11 | 63 |
|
paul@11 | 64 | moinmessage_gpg_homedir
|
paul@11 | 65 | This sets the path to the homedir initialised above.
|
paul@11 | 66 |
|
paul@11 | 67 | moinmessage_gpg_users_page (optional, default is MoinMessageUserDict)
|
paul@11 | 68 | This provides a mapping from key fingerprints to Moin usernames.
|
paul@11 | 69 |
|
paul@12 | 70 | moinmessage_gpg_signing_users_page (optional, default is MoinMessageSigningUserDict)
|
paul@12 | 71 | This provides a mapping from Moin usernames to key fingerprints.
|
paul@12 | 72 |
|
paul@12 | 73 | moinmessage_gpg_recipients_page (optional, default is MoinMessageRecipientsDict)
|
paul@12 | 74 | This provides a mapping from recipients to remote URLs and key fingerprints.
|
paul@37 | 75 | Each user can define the named page as a subpage of their own home page.
|
paul@37 | 76 | If no such personal mapping exists, a common mapping exists relative to the
|
paul@37 | 77 | site root.
|
paul@12 | 78 |
|
paul@20 | 79 | moinmessage_reject_messages_without_dates (optional, default is True)
|
paul@20 | 80 | This causes messages sent to a Wiki using the PostMessage action to be
|
paul@20 | 81 | rejected if date information is missing.
|
paul@20 | 82 |
|
paul@38 | 83 | For signature verification to function, the following needs to be added:
|
paul@38 | 84 |
|
paul@38 | 85 | from MoinMoin.auth.pgp import PGPAuth
|
paul@38 | 86 |
|
paul@38 | 87 | This should import an authentication handler installed when the MoinMessage
|
paul@38 | 88 | software is installed as an extension package.
|
paul@38 | 89 |
|
paul@38 | 90 | Within the configuration class itself, the auth setting needs to be updated to
|
paul@38 | 91 | include PGPAuth in the list of registered handlers. For example:
|
paul@38 | 92 |
|
paul@38 | 93 | auth = [MoinAuth(), PGPAuth()]
|
paul@38 | 94 |
|
paul@38 | 95 | This would permit the traditional Moin authentication and add signature-based
|
paul@38 | 96 | authentication so that messages can be accepted by the Wiki.
|
paul@38 | 97 |
|
paul@12 | 98 | Fingerprints and Keys
|
paul@12 | 99 | ---------------------
|
paul@12 | 100 |
|
paul@12 | 101 | All fingerprints mentioned in the various configuration pages must exclude
|
paul@12 | 102 | space characters - that is, the letters and digits must appear together in a
|
paul@12 | 103 | continuous block of text - and refer to keys available in the Wiki homedir.
|
paul@12 | 104 |
|
paul@11 | 105 | The Fingerprint-to-Username Mapping
|
paul@11 | 106 | -----------------------------------
|
paul@11 | 107 |
|
paul@12 | 108 | The mapping from fingerprints to usernames typically defined by the
|
paul@12 | 109 | MoinMessageUserDict page is a WikiDict having the following general format:
|
paul@11 | 110 |
|
paul@11 | 111 | fingerprint:: username
|
paul@11 | 112 |
|
paul@12 | 113 | Each fingerprint corresponds to a key used by a person wanting to send
|
paul@12 | 114 | messages to the Wiki to sign such messages.
|
paul@11 | 115 |
|
paul@11 | 116 | Each username must correspond to a registered user in the Wiki.
|
paul@11 | 117 |
|
paul@12 | 118 | The Username-to-Signing Key Mapping
|
paul@12 | 119 | -----------------------------------
|
paul@12 | 120 |
|
paul@12 | 121 | The mapping from usernames to fingerprints typically defined by the
|
paul@12 | 122 | MoinMessageSigningUserDict page is a WikiDict having the following general
|
paul@12 | 123 | format:
|
paul@12 | 124 |
|
paul@12 | 125 | username:: fingerprint
|
paul@12 | 126 |
|
paul@12 | 127 | Each fingerprint corresponds to a key available in the Wiki's GPG homedir
|
paul@12 | 128 | generated for the purpose of signing the specified user's messages. Such a key
|
paul@12 | 129 | is not the same as one used by a person to send messages to the Wiki since
|
paul@12 | 130 | only the public key used to verify such messages should be known to the Wiki.
|
paul@12 | 131 |
|
paul@12 | 132 | The Recipients Mapping
|
paul@12 | 133 | ----------------------
|
paul@12 | 134 |
|
paul@12 | 135 | The mapping from recipients to remote URLs and fingerprints typically defined
|
paul@12 | 136 | by the MoinMessageRecipientsDict page is a WikiDict having the following
|
paul@12 | 137 | general format:
|
paul@12 | 138 |
|
paul@27 | 139 | recipient:: location ... [ fingerprint ]
|
paul@27 | 140 |
|
paul@27 | 141 | Locations are specified as follows:
|
paul@27 | 142 |
|
paul@27 | 143 | type=value
|
paul@27 | 144 |
|
paul@28 | 145 | Where the type is "page", the accompanying value must be a page name
|
paul@28 | 146 | indicating a page that provides a message store that will accept messages.
|
paul@12 | 147 |
|
paul@27 | 148 | Where the type is "url", the accompanying value must be a URL that must itself
|
paul@27 | 149 | refer to a resource that can accept MoinMessage content.
|
paul@12 | 150 |
|
paul@27 | 151 | Where a location of type "url" has been given, a fingerprint must accompany
|
paul@27 | 152 | this information in order to encrypt messages sent to the specified resource.
|
paul@27 | 153 |
|
paul@27 | 154 | Each fingerprint corresponds to a key used by the Wiki to encrypt messages and
|
paul@27 | 155 | by the remote site (as identified by the URL) to decrypt messages.
|
paul@12 | 156 |
|
paul@11 | 157 | Quick Start: Signing, Encrypting and Sending Messages
|
paul@11 | 158 | -----------------------------------------------------
|
paul@11 | 159 |
|
paul@11 | 160 | To send a message signed and encrypted to a resource on localhost:
|
paul@11 | 161 |
|
paul@12 | 162 | python tests/test_send.py 1C1AAF83 0891463A http://localhost/wiki/ShareTest \
|
paul@16 | 163 | collection update 'An update to the Wiki.' 'Another update.'
|
paul@11 | 164 |
|
paul@11 | 165 | Here, the first identifier is a reference to the signing key (over which you
|
paul@11 | 166 | have complete control), and the second identifier is a reference to the
|
paul@11 | 167 | encryption key (which is a public key published for the Wiki).
|
paul@11 | 168 |
|
paul@11 | 169 | This needs password protection to be removed from the secret key in the Web
|
paul@12 | 170 | server environment. It also uses a modified trust model when invoking gpg in
|
paul@12 | 171 | order to avoid complaints about the identity of the sender during encryption.
|
paul@11 | 172 |
|
paul@11 | 173 | Below, the mechanisms employed are illustrated through the use of the other
|
paul@11 | 174 | test programs.
|
paul@11 | 175 |
|
paul@11 | 176 | Signing
|
paul@11 | 177 | -------
|
paul@11 | 178 |
|
paul@11 | 179 | Prepare a message signed with a "detached signature" (note that this does not
|
paul@11 | 180 | seem to be what gpg calls a detached signature with the --detach-sig option):
|
paul@11 | 181 |
|
paul@16 | 182 | python tests/test_message.py collection update 'An update to the Wiki.' \
|
paul@16 | 183 | 'Another update.' \
|
paul@11 | 184 | | python tests/test_sign.py 1C1AAF83
|
paul@11 | 185 |
|
paul@11 | 186 | The complicated recipe based on the individual operations is as follows:
|
paul@11 | 187 |
|
paul@16 | 188 | python tests/test_message.py collection update 'An update to the Wiki.' \
|
paul@16 | 189 | 'Another update.' \
|
paul@11 | 190 | > test.txt \
|
paul@11 | 191 | && cat test.txt \
|
paul@11 | 192 | | gpg --armor -u 1C1AAF83 --detach-sig \
|
paul@11 | 193 | | python tests/test_sign_wrap.py test.txt
|
paul@11 | 194 |
|
paul@11 | 195 | Encryption
|
paul@11 | 196 | ----------
|
paul@11 | 197 |
|
paul@11 | 198 | Prepare a message with an encrypted payload using the above key:
|
paul@11 | 199 |
|
paul@16 | 200 | python tests/test_message.py collection update 'An update to the Wiki.' \
|
paul@16 | 201 | 'Another update.' \
|
paul@11 | 202 | | python tests/test_encrypt.py 0891463A
|
paul@11 | 203 |
|
paul@11 | 204 | The complicated recipe based on the individual operations is as follows:
|
paul@11 | 205 |
|
paul@16 | 206 | python tests/test_message.py collection update 'An update to the Wiki.' \
|
paul@16 | 207 | 'Another update.' \
|
paul@11 | 208 | > test.txt \
|
paul@11 | 209 | && cat test.txt \
|
paul@11 | 210 | | gpg --armor -r 0891463A --encrypt --trust-model always \
|
paul@11 | 211 | | python tests/test_encrypt_wrap.py
|
paul@11 | 212 |
|
paul@11 | 213 | Note that "--trust-model always" is used only to avoid prompting issues.
|
paul@11 | 214 |
|
paul@11 | 215 | Signing and Encrypting
|
paul@11 | 216 | ----------------------
|
paul@11 | 217 |
|
paul@12 | 218 | Sign and encrypt a message:
|
paul@11 | 219 |
|
paul@16 | 220 | python tests/test_message.py collection update 'An update to the Wiki.' \
|
paul@16 | 221 | 'Another update.' \
|
paul@11 | 222 | | python tests/test_sign.py 1C1AAF83 \
|
paul@11 | 223 | | python tests/test_encrypt.py 0891463A
|
paul@11 | 224 |
|
paul@11 | 225 | The complicated recipe based on the individual operations is as follows:
|
paul@11 | 226 |
|
paul@16 | 227 | python tests/test_message.py collection update 'An update to the Wiki.' \
|
paul@16 | 228 | 'Another update.' \
|
paul@11 | 229 | > test.txt \
|
paul@11 | 230 | && cat test.txt \
|
paul@11 | 231 | | gpg --armor -u 1C1AAF83 --detach-sig \
|
paul@11 | 232 | | python tests/test_sign_wrap.py test.txt \
|
paul@11 | 233 | | gpg --armor -r 0891463A --encrypt --trust-model always \
|
paul@11 | 234 | | python tests/test_encrypt_wrap.py
|
paul@11 | 235 |
|
paul@11 | 236 | Posting a Message
|
paul@11 | 237 | -----------------
|
paul@11 | 238 |
|
paul@11 | 239 | To post a signed and/or encrypted message, output from the above activities
|
paul@11 | 240 | can be piped into the following command:
|
paul@11 | 241 |
|
paul@12 | 242 | python tests/test_post.py http://localhost/wiki/ShareTest
|
paul@11 | 243 |
|
paul@11 | 244 | Here, the resource "/wiki/ShareTest" on localhost is presented with the
|
paul@11 | 245 | message.
|
paul@16 | 246 |
|
paul@32 | 247 | Fetching Messages
|
paul@32 | 248 | -----------------
|
paul@32 | 249 |
|
paul@32 | 250 | To fetch messages from a message store associated with a page, the following
|
paul@32 | 251 | command can be used:
|
paul@32 | 252 |
|
paul@32 | 253 | python tests/test_fetch.py 1C1AAF83 0891463A http://localhost/wiki/ShareTest \
|
paul@32 | 254 | RETR
|
paul@32 | 255 |
|
paul@32 | 256 | This should retrieve all messages from the store associated with the
|
paul@32 | 257 | "/wiki/ShareTest" resource on localhost.
|
paul@32 | 258 |
|
paul@16 | 259 | The Message Format
|
paul@16 | 260 | ------------------
|
paul@16 | 261 |
|
paul@16 | 262 | Messages are MIME-encoded and consist of one or more update fragments. Where
|
paul@16 | 263 | the "Update-Type" header is present and set to a value of "collection", a
|
paul@16 | 264 | multipart message describes as many updates as there are parts. Otherwise,
|
paul@16 | 265 | only a single update is described by the message.
|
paul@16 | 266 |
|
paul@16 | 267 | For each update, the "Update-Action" header indicates the action to be taken
|
paul@16 | 268 | with the update content. Where it is absent, the content is inserted into the
|
paul@16 | 269 | Wiki page specified in the request; where it is present and set to "replace",
|
paul@16 | 270 | the content replaces all content on the Wiki page; where it is set to "store",
|
paul@16 | 271 | the content is stored in a message store associated with the Wiki page.
|
paul@16 | 272 |
|
paul@16 | 273 | Each update may describe multiple representations of some content by employing
|
paul@16 | 274 | a multipart section containing parts for each of the representations.
|
paul@16 | 275 | Alternatively, a single message part may describe a single representation.
|