Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I'm trying to decode an X.509 certificate and have a question regarding the decoding of the extensions. In particular, the octet string corresponding to id-ce-keyUsage (OID 2.5.29.15) is as follows:

03020106

03 is the tag. A BIT STRING. 02 is the length. The value is 0106. Here's what http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf says about the encoding of bitstring's:

"The initial octet shall encode, as an unsigned binary integer with bit 1 as the least significant bit, the number of
unused bits in the final subsequent octet. The number shall be in the range zero to seven."

Here's the ASN.1 definition for this particular BIT STRING (from http://tools.ietf.org/html/rfc5280#appendix-A.1):

-- key usage extension OID and syntax

id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }

KeyUsage ::= BIT STRING {
     digitalSignature        (0),
     nonRepudiation          (1),  -- recent editions of X.509 have
                                -- renamed this bit to contentCommitment
     keyEncipherment         (2),
     dataEncipherment        (3),
     keyAgreement            (4),
     keyCertSign             (5),
     cRLSign                 (6),
     encipherOnly            (7),
     decipherOnly            (8) }

My question is... why does the final bitstring have one unused byte? My expectation would be that it'd have no unused bits since the ASN.1 definition defines values for all eight bits.

share|improve this question

1 Answer 1

up vote 3 down vote accepted

In ASN.1, there are two distinct types which are designated by BIT STRING.

If you have this:

Foo ::= BIT STRING

then Foo is a type for a generic string of bits, with an arbitrary length, and arbitrary values for each bit.

On the other hand, with this:

Bar ::= BIT STRING {
    x  (0),
    y  (1),
    z  (2)
}

then Bar is a type for a set of flags, and DER encoding rules state that these should be encoded as if it was a generic BIT STRING, but stopping at the last non-zero bit. Thus, if you want to encode a value of type Bar with the flags x and y set, but not z, then the encoding must be a BIT STRING of length exactly two bits, not three or more. That's what DER mandates; the generic BER rules allow for adding as many extra bits of value zero that you wish. Upon decoding, flags which are beyond the encoded code length are assumed to be cleared.

So, for your BIT STRING: 03 02 01 06 decodes as such:

  • 03: a BIT STRING.
  • 02: the value consists in the next two bytes.
  • 01: first value byte; for a BIT STRING, it means that you shall ignore exactly 1 bit in the last value byte.
  • 06: the bits themselves.

In binary, 06 is 00000110, but the first value byte says that we shall ignore the last bit, to the bytes really encode a string of exactly 7 bits: 0000011.

The last bit of that string is a 1, so this is compatible with the DER rules. When interpreting the bit string as so many individual flags, the two '1' state that flags numbered 5 and 6 are set (that's keyCertSign and cRLSign, respectively, which is typical of CA certificates), while all other flags are cleared, even if their numerical index is larger then the actual encoded bit string.

BER would allow you to encode that KeyUsage value as, for instance:

03 04 05 06 00 00

i.e. a string of 19 bits (three value bytes with bits in them, but the last 5 are ignored). This would still indicate that the keyCertSign and cRLSign flags are set, so the semantics would be identical. However, certificate contents should follow DER, which mandates encoding to the minimal length (trailing zeroes being removed).

This particularity of DER encoding is meant for compatibility with later versions: DER encoding of a set of flags depends on which flags are actually set, not on which flags could have been set -- hence, a later X.509 version could add new possible key usages, without making invalid (i.e. non-DER) certificates which use the previous version.

Really, this would have been much clearer if ASN.1 syntax had not used "BIT STRING" for two distinct purposes. The latter usage (set of flags) should have used an explicit distinct keyword, e.g. "FLAGS".

share|improve this answer
    
I guess that'd be because of section 8.6.2.4 in the BER/DER PDF. "Where ITU-T Rec. X.680 | ISO/IEC 8824-1, 21.7, applies a BER encoder/decoder can add or remove trailing 0 bits from the value." That's one of the worst written sections in that whole doc. The fact that it said BER - a superset of DER - made me think it didn't apply to DER. Never mind the fact that X.680 - not X.690 - that provides this insight. –  compcert Jan 6 '12 at 8:50

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.