Mobility of Signed Resources
By John Boyer

It is generally useful to be able to move signed resources without breaking signatures on the data. As long as the resource can still be obtained by a given Location, it should be verifiable. Also, it will often be necessary to change the Transforms applied to the data since Transforms help to locate the data and, as well, the data may be stored in a different format in its new location.

P.S. Many have expressed concern about the solution proposed in this document, which is to apply Transforms to SignedInfo. The following notes apply:

  1. The solution is more powerful than necessary but leverages the existing syntax for omission logic
  2. It was my understanding that either a slight markup redesign or an 'exception' would have to be made to ensure that the SignedInfo Transforms do not omit themselves from the SignedInfo text that is digested in SignatureValue. The attacks so far on the SignedInfo transforms have come from
  3. XPath offers precision syntax for specifying what to omit. A 'document closure' argument could therefore be made on SignedInfo. Yes, if the changes made to SignedInfo are too radical, then security is lost, but security analysis of the offending application will reveal this. Conversely, if only precisely defined omissions can be made from SignedInfo, then it can be asserted that those omissions cannot impact the security of SignedInfo. That is the essence of the document closure argument.
  4. The assumption of the need for this feature is that ObjectReference Location and Transforms specify how core code will obtain the bits that were digested in DigestValue. If we look at Location and Transforms as 'hints' on how a verifier can obtain the bag of bits to verify, then an application can do whatever is necessary to obtain the bits to validate, but the signatures are less likely to cross-validate among differing applications without tight coupling of those applications. This, of course, undermines the notion of creating a standard in the first place.

The point of this list is that you don't have to agree with Transforms on SignedInfo to take something meaningful out of this document. These scenarios are problems that people have that we must either decide not to solve or decide to solve in some way. The SignedInfo Transforms describe what needs to be omitted from SignedInfo under the assumption that an ObjectReference's Location and Transforms are more than just hints for the verifier.

1. Volatile Web URLs

If a signature is expected to be verifiable after the indicated resource is moved to a new web location, then there must be a way of changing how the resource is retrieved. Deferring to application-specific URN processing rules may not suffice given the frequency with which this situation occurs on the web. An alternative is to allow a Transforms element to process the SignedInfo.

<Signature>
<SignedInfo>

<CanonicalizationMethod...
<!-- This can go in the following Transforms -->

<Transforms> <!-- On SignedInfo -->
<Transform Algorithm="&XPath;">
descendant-or-self::node()[
not(self::Location and parent::ObjectReference)]
</Transform>
</Transforms>

<SignatureMethod...

<ObjectReference Location="http://some.org/some.doc">

<DigestMethod...
<DigestValue...

</ObjectReference>
</SignedInfo>

<SignatureValue...

</Signature>

Since the Location is omitted from the SignedInfo, if the resource is moved, the Location attribute can be changed without breaking the signature.


2. Conglomeration of Signed Records

It is useful for applications to put a number of signed records (perhaps the result of a database search) into the same XML file for delivery to a user without breaking the signatures on the individual records.

This can be accomplished by Id attribute omission in conjunction with omitting a small part of the Transforms of the ObjectReference. Specifically, if text of the the barename XPointer that identifies the element to be digested should be omitted. This will allow us to change that record name to DefendantRecord1, DefendantRecord2, etc.

<Signature>
<SignedInfo>

<CanonicalizationMethod...
<!-- This can go in the following Transforms -->

<Transforms> <!-- On SignedInfo -->
<Transform Algorithm="&XPath;">
descendant-or-self::node()[
not(self::text() and
ancestor::Transform[ancestor::ObjectReference[3] and
parent::Transform[@Algorithm="&XPtr;"])]
</Transform>
</Transforms>

<SignatureMethod...

<ObjectReference Location="">

<Transforms>
<Transform Algorithm="&XPtr;">DefendantRecord</Transform>
<Transform Algorithm="&XPath;">
descendant-or-self::node()[not(self::Id)]</Transform>
</Transforms>

<DigestMethod...
<DigestValue...

</ObjectReference>
</SignedInfo>

<SignatureValue...

</Signature>

<Record Id="DefendantRecord">
<name>Someone Guilty</name>
<address>#3 Death Row</address>
<status>appeal of conviction</status>
...
</Record>

If a database search yielded a number of these records, then placing them in a single file for return to the user would require changing each Id to something unique due to rules of XML. This can be done if the Id attribute is omitted from the message whose digest is placed in the DigestValue of ObjectReference and if the text of the XPointer Transform in the ObjectReference is omitted from the SignedInfo before its digest is calculated and placed in SignatureValue.

Actually, the XPath transform of SignedInfo could be even more explicit. The XPath could restrict the omission to being the first Transform in the ObjectReference's Transforms, and it could even do some string matching on the contents of the text before deciding to omit it.

This is a case of applying the notion of DOCUMENT CLOSURE to SignedInfo. As long as the SignedInfo Transforms are signed, we can guarantee that the small bit of data omitted from ObjectReference Transforms does not break security even though it is not covered by the digest.

3. Enveloped Internal to Unenveloped External

A comprehensive scenario uses the XML signature as an envelope for delivery of data to the desktop. Once at the desktop, the data must be unenveloped and detached from the signature for use by applications.

In this case, the signature will be retained for future use, but it must be changed to indicate the new external location of the signed data, and the enveloped copy of the data will be deleted.

In this first signature, the data is base64 encoded and enveloped in an element called DATA1. The Transforms in the ObjectReference recover the original binary data on which the digest will be calculated.

The Transforms outside of the ObjectReference represent the proposed method for helping to solve this problem. Since the Location and Transforms must be changed when the data is moved outside of the enveloping Object, they are omitted from the SignedInfo.

<Signature>
<SignedInfo>

<CanonicalizationMethod...
<!-- This can go in the following Transforms -->

<Transforms> <!-- On SignedInfo -->
<Transform Algorithm="&XPath;">
descendant-or-self::node()[
not(self::Location and parent::ObjectReference) and
not(Exact description of the base 64 and Xpath transforms below)]
</Transform>
</Transforms>

<SignatureMethod...

<ObjectReference Location="">

<Transforms>
<Transform Algorithm="&XPath;">
descendant::text()[parent::node()[@Id="DATA1"]]
</Transform>
<Transform Algorithm="&base64;"/>
</Transforms>

<DigestMethod...
<DigestValue...

</ObjectReference>
</SignedInfo>

<SignatureValue...

<Object Id="DATA1" Encoding="&base64;">
ABCDEFGHIJKLMNOPQRSTUVWXYZ...
</Object>

</Signature>

When the data is moved to disk in natural binary format, the Location can be changed to indicate the local file, and the specific Transforms in ObjectReference are no longer necessary:

<Signature>
<SignedInfo>

<CanonicalizationMethod...
<!-- This can go in the following Transforms -->

<Transforms> <!-- On SignedInfo -->
<Transform Algorithm="&XPath;">
descendant-or-self::node()[
not(self::Location and parent::ObjectReference) and
not(ancestor::Transforms[parent::ObjectReference])]
</Transform>
</Transforms>

<SignatureMethod...

<ObjectReference Location="file:filename">

<DigestMethod...
<DigestValue...

</ObjectReference>
</SignedInfo>

<SignatureValue...

</Signature>


Conclusion