Tell P7M and P7S apart

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Tell P7M and P7S apart

Giacomo Boccardo
Hello everybody,

    reading as few bytes as possible, how can I tell a P7M and a P7S
(P7M detached) apart? Please consider all the possible situations
(optional content, content present but empty).

Kind regards,

    Giacomo



Reply | Threaded
Open this post in threaded view
|

Re: Tell P7M and P7S apart

Giacomo Boccardo
I came up with a first solution which works on all the files I have, but I'm not extremely confident in general :)

The following method takes as argument the stream when it has already been consumed reaching the element:

EncapsulatedContentInfo ::= SEQUENCE {
     eContentType ContentType,
     eContent [0] EXPLICIT OCTET STRING OPTIONAL }


  private boolean isContentEmpty(final InputStream is) throws IOException, FormatException {
        final int tagSeq = is.read();

        // Just to skip it (method from ASN1InputStream)
        readTagNumber(is, tagSeq);

        final int length = is.read();

        // Content is empty if:
        // indefinite-length: after OID PKCSObjectIdentifiers.data there is EOC (0x00 0x00)
        // definite-length: length equals 11 (there is only OID PKCSObjectIdentifiers.data)

        // indefinite-length
        if (length == 0x80) {
            // Is there an easier way to read/skip PKCSObjectIdentifiers.data?
            int n = 0;
            int tag;
            do {
                tag = is.read();
                // PKCSObjectIdentifiers.data = 1.2.840.113549.1.7.1
                if (1 == tag && (0 == n || 2 == n) || 7 == tag && 1 == n) {
                    n = n + 1;
                }
            } while (n < 3);

            final int byte1 = is.read();
            final int byte2 = is.read();

            // EOC
            return 0 == byte1 && 0 == byte2;
        }

        // definite-length
        return length == 11;
    }


Any suggestion or insult? :)


Thanks,
    Giacomo

Giacomo Boccardo wrote on 31/05/2017 21:03:
Hello everybody,

    reading as few bytes as possible, how can I tell a P7M and a P7S
(P7M detached) apart? Please consider all the possible situations
(optional content, content present but empty).

Kind regards,

    Giacomo



Reply | Threaded
Open this post in threaded view
|

Re: Tell P7M and P7S apart

Peter Dettman-3
Hi Giacomo,
I suggest not messing around directly with ASN.1 tags and lengths.
Instead, you could make use of the ASN1StreamParser class (and other
*Parser classes in org.bouncycastle.asn1 package).

Refer to org.bouncycastle.asn1.cms.ContentInfoParser class, and how it's
used from org.bouncycastle.cms.CMSContentInfoParser (this one's in the
pkix jar).

Something like this:

boolean isContentEmpty(InputStream seqStream) {
        ASN1StreamParser p = new ASN1StreamParser(seqStream);
        ASN1SequenceParser seq = (ASN1SequenceParser)p.readObject();
        ASN1ObjectIdentifier contentType = (ASN1ObjectIdentifier)seq.readObject();
        ASN1TaggedObjectParser content = (ASN1TaggedObjectParser)seq.readObject();
        return content == null;
}

These *Parser classes are designed for lazy stream-parsing; in
particular the final readObject call (for 'content') is only reading the
tag/length (if the content is present) before returning a parser (or null).

Regards,
Pete Dettman


On 1/06/2017 5:27 AM, Giacomo Boccardo wrote:

> I came up with a first solution which works on all the files I have, but
> I'm not extremely confident in general :)
>
> The following method takes as argument the stream when it has already
> been consumed reaching the element:
>
> EncapsulatedContentInfo ::= SEQUENCE {
>      eContentType ContentType,
>      eContent [0] EXPLICIT OCTET STRING OPTIONAL }
>
>
>
>   private boolean isContentEmpty(final InputStream is) throws
> IOException, FormatException {
>         final int tagSeq = is.read();
>
>         // Just to skip it (method from ASN1InputStream)
>         readTagNumber(is, tagSeq);
>
>         final int length = is.read();
>
>         // Content is empty if:
>         // indefinite-length: after OID PKCSObjectIdentifiers.data there
> is EOC (0x00 0x00)
>         // definite-length: length equals 11 (there is only OID
> PKCSObjectIdentifiers.data)
>
>         // indefinite-length
>         if (length == 0x80) {
>             // Is there an easier way to read/skip
> PKCSObjectIdentifiers.data?
>             int n = 0;
>             int tag;
>             do {
>                 tag = is.read();
>                 // PKCSObjectIdentifiers.data = 1.2.840.113549.1.7.1
>                 if (1 == tag && (0 == n || 2 == n) || 7 == tag && 1 == n) {
>                     n = n + 1;
>                 }
>             } while (n < 3);
>
>             final int byte1 = is.read();
>             final int byte2 = is.read();
>
>             // EOC
>             return 0 == byte1 && 0 == byte2;
>         }
>
>         // definite-length
>         return length == 11;
>     }
>
>
> Any suggestion or insult? :)
>
>
> Thanks,
>     Giacomo
>
> Giacomo Boccardo wrote on 31/05/2017 21:03:
>> Hello everybody,
>>
>>     reading as few bytes as possible, how can I tell a P7M and a P7S
>> (P7M detached) apart? Please consider all the possible situations
>> (optional content, content present but empty).
>>
>> Kind regards,
>>
>>     Giacomo
>>
>>
>


Reply | Threaded
Open this post in threaded view
|

Re: Tell P7M and P7S apart

Giacomo Boccardo
Hi Peter,

    thanks for your solution, but unfortunately I've already tried that
and it doesn't work if you have definite lengths.

I have many

java.io.IOException: corrupted stream - out of bounds length found

on the second instruction because

int length = ASN1InputStream.readLength(_in, _limit);

is called and if you don't have enough bytes on the stream it raises
that exception.

If I pass all the stream it works, but I should investigate further to
be sure that all those calls are really lazy: I need a quick detection
using as few bytes as possible.


Kind regards,
    Giacomo


Peter Dettman wrote on 04/06/2017 17:50:

> Hi Giacomo,
> I suggest not messing around directly with ASN.1 tags and lengths.
> Instead, you could make use of the ASN1StreamParser class (and other
> *Parser classes in org.bouncycastle.asn1 package).
>
> Refer to org.bouncycastle.asn1.cms.ContentInfoParser class, and how it's
> used from org.bouncycastle.cms.CMSContentInfoParser (this one's in the
> pkix jar).
>
> Something like this:
>
> boolean isContentEmpty(InputStream seqStream) {
> ASN1StreamParser p = new ASN1StreamParser(seqStream);
> ASN1SequenceParser seq = (ASN1SequenceParser)p.readObject();
> ASN1ObjectIdentifier contentType = (ASN1ObjectIdentifier)seq.readObject();
> ASN1TaggedObjectParser content = (ASN1TaggedObjectParser)seq.readObject();
> return content == null;
> }
>
> These *Parser classes are designed for lazy stream-parsing; in
> particular the final readObject call (for 'content') is only reading the
> tag/length (if the content is present) before returning a parser (or null).
>
> Regards,
> Pete Dettman
>
>
> On 1/06/2017 5:27 AM, Giacomo Boccardo wrote:
>> I came up with a first solution which works on all the files I have, but
>> I'm not extremely confident in general :)
>>
>> The following method takes as argument the stream when it has already
>> been consumed reaching the element:
>>
>> EncapsulatedContentInfo ::= SEQUENCE {
>>      eContentType ContentType,
>>      eContent [0] EXPLICIT OCTET STRING OPTIONAL }
>>
>>
>>
>>   private boolean isContentEmpty(final InputStream is) throws
>> IOException, FormatException {
>>         final int tagSeq = is.read();
>>
>>         // Just to skip it (method from ASN1InputStream)
>>         readTagNumber(is, tagSeq);
>>
>>         final int length = is.read();
>>
>>         // Content is empty if:
>>         // indefinite-length: after OID PKCSObjectIdentifiers.data there
>> is EOC (0x00 0x00)
>>         // definite-length: length equals 11 (there is only OID
>> PKCSObjectIdentifiers.data)
>>
>>         // indefinite-length
>>         if (length == 0x80) {
>>             // Is there an easier way to read/skip
>> PKCSObjectIdentifiers.data?
>>             int n = 0;
>>             int tag;
>>             do {
>>                 tag = is.read();
>>                 // PKCSObjectIdentifiers.data = 1.2.840.113549.1.7.1
>>                 if (1 == tag && (0 == n || 2 == n) || 7 == tag && 1 == n) {
>>                     n = n + 1;
>>                 }
>>             } while (n < 3);
>>
>>             final int byte1 = is.read();
>>             final int byte2 = is.read();
>>
>>             // EOC
>>             return 0 == byte1 && 0 == byte2;
>>         }
>>
>>         // definite-length
>>         return length == 11;
>>     }
>>
>>
>> Any suggestion or insult? :)
>>
>>
>> Thanks,
>>     Giacomo
>>
>> Giacomo Boccardo wrote on 31/05/2017 21:03:
>>> Hello everybody,
>>>
>>>     reading as few bytes as possible, how can I tell a P7M and a P7S
>>> (P7M detached) apart? Please consider all the possible situations
>>> (optional content, content present but empty).
>>>
>>> Kind regards,
>>>
>>>     Giacomo
>>>
>>>


Reply | Threaded
Open this post in threaded view
|

Re: Tell P7M and P7S apart

Peter Dettman-3
Hi Giacomo,

There's no reason it shouldn't work with definite lengths I assume you
are seeing the "corrupted stream" exception because you are only passing
in the beginning of the file, or fiddling with the stream in some other way.

In any case, to avoid the bounds checking, you can use:
        new ASN1StreamParser(seqStream, Integer.MAX_VALUE);

I suppose the code I gave is not strictly the minimum number of bytes
possible, since it will read the tag and length of the 'content' tagged
object, when in theory you only need to read (or not) the first byte of
its tag.

If somehow those extra few bytes are important, you might need to copy
some of the internal code and adapt it to the bare minimum.

Regards,
Pete Dettman


On 5/06/2017 2:56 AM, Giacomo Boccardo wrote:

> Hi Peter,
>
>     thanks for your solution, but unfortunately I've already tried that
> and it doesn't work if you have definite lengths.
>
> I have many
>
> java.io.IOException: corrupted stream - out of bounds length found
>
> on the second instruction because
>
> int length = ASN1InputStream.readLength(_in, _limit);
>
> is called and if you don't have enough bytes on the stream it raises
> that exception.
>
> If I pass all the stream it works, but I should investigate further to
> be sure that all those calls are really lazy: I need a quick detection
> using as few bytes as possible.


Reply | Threaded
Open this post in threaded view
|

Re: Tell P7M and P7S apart

Giacomo Boccardo
On 06/05/17 15:06:45, Peter Dettman wrote:
> Hi Giacomo,
Hi Peter,
>
> There's no reason it shouldn't work with definite lengths I assume you
> are seeing the "corrupted stream" exception because you are only passing
> in the beginning of the file, or fiddling with the stream in some other way.
That's exactly the reason.
> In any case, to avoid the bounds checking, you can use:
> new ASN1StreamParser(seqStream, Integer.MAX_VALUE);
>
> I suppose the code I gave is not strictly the minimum number of bytes
> possible, since it will read the tag and length of the 'content' tagged
> object, when in theory you only need to read (or not) the first byte of
> its tag.
> If somehow those extra few bytes are important, you might need to copy
> some of the internal code and adapt it to the bare minimum.
A few extra bytes are not a problem: I simply need not to read all the
content just to test if it's present :)
If it works like that, it's surely better than reading it as I did.
>
> Regards,
> Pete Dettman
>
Kind regards,
     Giacomo

> On 5/06/2017 2:56 AM, Giacomo Boccardo wrote:
>> Hi Peter,
>>
>>      thanks for your solution, but unfortunately I've already tried that
>> and it doesn't work if you have definite lengths.
>>
>> I have many
>>
>> java.io.IOException: corrupted stream - out of bounds length found
>>
>> on the second instruction because
>>
>> int length = ASN1InputStream.readLength(_in, _limit);
>>
>> is called and if you don't have enough bytes on the stream it raises
>> that exception.
>>
>> If I pass all the stream it works, but I should investigate further to
>> be sure that all those calls are really lazy: I need a quick detection
>> using as few bytes as possible.
>