Engine/SignedOnly
This is a copied draft from a ticket, ignore for now
We now have (hopefully) the ability to create signed-only messages from the engine. However, this will require the MIME generators above the engine to recognise signed-only messages and render them properly, which may create some top-level issues. This post:
- Describes how to trigger signed-only behaviour in encrypt_message
- Describes what a signed-only message will look like when returned to the caller of encrypt_message
- Describes what needs to be done to render the message properly
I cannot anticipate what is necessary to make this work for you at the top-level - what I’m writing here is based on what I had to do to force libetpan to render it.
EDIT: I will add a top-level function, message_sign_only(session, … (signature TBD - will file separate ticket for adapters)) so that signed_only doesn’t have to go through encrypt_message directly, which is semantically confusing I think. Will link a separate ticket to that.
However, another way to trigger signed-only behaviour right now is to pass PEP_enc_sign_only as the encryption type into encrypted message. In fact, except for messages with errors, any message which doesn’t get encrypted (maybe due to lack of keys for recips) right now will be returned signed and with a value of PEP_UNENCRYPTED (EDIT: I will add PEP_SIGNED_ONLY for messages which are either intended to be signed_only or which were fed to encrypt_message and could not be encrypted for lack of keys, and will thus be only signed). This will probably not impact how those messages will be treated, but it will let you know there is a message which has changed which should be sent rather than the original, or if you WANT a signed message, it will let you know that was successful.
I’ll explain in more detail, but msg->enc_format will be PEP_enc_sign_only, msg->longmsg will contain the verbatim, already-encoded, signed text, msg->attachments->value is the value of the micalg parameter needed by multipart/signed information, and msg->attachments->next is the pgp signature.
Let’s start with a plaintext message consisting of just a message and a key.
From: Alice in Wonderland <pep.test.alice@pep-project.org>
To: Alice in Wonderland <pep.test.alice@pep-project.org>
Subject: Sign this, baby
X-pEp-Version: 2.1
Content-Type: multipart/mixed; boundary="6b8b4567327b23c6643c986966334873"
--6b8b4567327b23c6643c986966334873
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline; filename="msg.txt"
Nah really, sign this.
--6b8b4567327b23c6643c986966334873
Content-Type: application/pgp-keys
Content-Disposition: attachment; filename="pEpkey.asc"
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQEN... ... ...
=Wx0T
-----END PGP PUBLIC KEY BLOCK-----
--6b8b4567327b23c6643c986966334873--
The MIME tree here is simple:
multpart/mixed
|__ text/plain (the message text)
|__ application/pgp-keys (the key)
In order to sign this message, the signed text is this whole tree without the preceding headers, so everything from ‘Content-Type: multipart/mixed; boundary=“6b8b4567327b23c6643c986966334873”’ down. You will see in a moment why this gets complicated.
What the engine does is render the above MIME tree as plaintext (so basically whatever message object you pass in is turned into a plaintext message without headers) and generate a signature for that block of text which can then no longer be modified by anything further without breaking the signature. The rendered text of the message is in message->longmsg on return. The attachments object contains two attachments, one of which is a throwaway containing information necessary to render the message (the value of the “micalg” parameter, indicating the hash algorithm used to make the signature), and the other is a signature of type “application/pgp-signature” containing the signature.
Let’s look at what this is supposed to look like when rendered as a signed message before I go further:
From: Alice in Wonderland <pep.test.alice@pep-project.org>
To: Alice in Wonderland <pep.test.alice@pep-project.org>
Subject: Sign this, baby
X-pEp-Version: 2.1
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="74b0dc5119495cff2ae8944a625558ec";
protocol="application/pgp-signature"; micalg="pgp-sha256"
--74b0dc5119495cff2ae8944a625558ec
Content-Type: multipart/mixed; boundary="6b8b4567327b23c6643c986966334873"
--6b8b4567327b23c6643c986966334873
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline; filename="msg.txt"
Nah really, sign this.
--6b8b4567327b23c6643c986966334873
Content-Type: application/pgp-keys
Content-Disposition: attachment; filename="pEpkey.asc"
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQEN... ... ...
=Wx0T
-----END PGP PUBLIC KEY BLOCK-----
--6b8b4567327b23c6643c986966334873--
--74b0dc5119495cff2ae8944a625558ec
Content-Type: application/pgp-signature
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="signature.asc"
-----BEGIN PGP SIGNATURE-----
iQEzBA... ... ...
=jY8q
-----END PGP SIGNATURE-----
--74b0dc5119495cff2ae8944a625558ec--
Note that now what the MIME tree looks like is this:
multipart/signed
|__ multpart/mixed
|__ text/plain (the message text)
|__ application/pgp-keys (the key)
|__ application/pgp-signature
And here’s where we hit the problem. If we could just generate that all at once, like normal, we’d be fine.
The problem is that the entire first subtree, including Content-type and other control information, is already rendered and cannot be changed, because we signed it.
So basically, you’ve got this to generate from:
multipart/signed
A BUNCH OF ALREADY GENERATED STUFF WHICH YOU HAVE TO TAKE VERBATIM
|__ application/pgp-signature
From the generator’s point of view, the problem is that it generates the control information for the main tree:
Content-Type: multipart/signed; boundary="74b0dc5119495cff2ae8944a625558ec";
protocol="application/pgp-signature"; micalg="pgp-sha256"
and then has to give the boundary showing it’s about to insert the first part:
--74b0dc5119495cff2ae8944a625558ec
But instead of recursively generating the tree node below it, including Content-Type etc, it has to take a long string and shove it in between two boundary markers verbatim before going back to normal tree-based generation (the application/pgp-signature part).
If your generator isn’t picky and lets you tell it to do whatever you want, this may be fine for you. I had to kick etpan a lot for it.
SO: Your job, should you choose to accept it is to do the following;
- Grab data value in the first attachment (of mime-type multipart/signed) to put in the parameter for the Content-type: multipart/signed parameters. (the format of the parameter to shove this string into is micalg=“”. Ignore this attachment for rendering purposes from here on.
- Somehow generate the multipart/signed content headers and boundary.
- ensure this boundary is rendered, and then the msg->longmsg text inserted verbatim as its content
- render the boundary again (how your implementation does this I don’t know, but usually it’s part of multipart generation)
- insert, as usual, msg->attachments->next as the second node of the multipart/signed tree
- pray a lot and be done (by ensuring the closing boundary tag goes afterwards)
Feel free to talk to me about this directly; I know it’s confusing.
This currently will only work against the ENGINE-633 branch (once pushed - let me get this done first)