The "extra" octet is needed because ASN.1 uses two's complement notation for integers, per section 8.3.3 of X.690:
The contents octets shall be a two's complement binary number equal to
the integer value
In two's complement, the highest bit indicates a negative number. Since none of our numbers are actually negative, the correct notation needs to have leading zeros.
We can see this in action on the following signature:
$ openssl asn1parse -i -in foo.csr -strparse 126 -out raw
0:d=0 hl=2 l= 69 cons: SEQUENCE
2:d=1 hl=2 l= 32 prim: INTEGER :1937197C689C3E511CCB830E5F3677D3FA18F045EBA969739B622B95FEB619C1
36:d=1 hl=2 l= 33 prim: INTEGER :BA1B75CEE9714860639A20B50125C20224CBBEBE01F4CCBFA46731706F55D171
You'll notice that my first integer has length 32 but the second has length 33.
Dumping the raw contents, we get:
$ hd raw
00000000 30 45 02 20 19 37 19 7c 68 9c 3e 51 1c cb 83 0e |0E. .7.|h.>Q....|
00000010 5f 36 77 d3 fa 18 f0 45 eb a9 69 73 9b 62 2b 95 |_6w....E..is.b+.|
00000020 fe b6 19 c1 02 21 00 ba 1b 75 ce e9 71 48 60 63 |.....!...u..qH`c|
00000030 9a 20 b5 01 25 c2 02 24 cb be be 01 f4 cc bf a4 |. ..%..$........|
00000040 67 31 70 6f 55 d1 71 |g1poU.q|
Let's break it down into the various components:
30 45
: SEQUENCE
of length 69
02 20
: INTEGER
of length 32
19 37 19 7c ... 19 c1
: the INTEGER
contents (32 bytes)
02 21
: INTEGER
of length 33
00 ba 1b 75 ... d1 71
: the INTEGER
contents (33 bytes)
This matches exactly what asn1parse
tells us. Notice the difference between the first integer (encoded to a length of 32 bytes) and the second (encoded to a length of 33 bytes).
The first one begins with 19
which is 00011001
in binary. Because it does not have a high bit set, this is indeed a positive number.
But the second one begins with BA
which is 10111010
in binary. The high bit is set which would be negative if we decoded it as is. Since it isn't negative, the correct two's complement notation has leading zeros.
In your case, both numbers have the high bit set, so they both get 00
prepended.
You end up with a total size of: $2 + 2 + 32+1 + 2 + 32+1 = 72$.
Oops, we're still one short of 73. This is part of the BITSTRING
encoding which is defined in the spec, section 8.6.2.2 as:
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.
This is because a BITSTRING
can represent a number of bits that is not a multiple of 8. In our case, we're making use of all bits, so the initial octet for the bitstring is 00
.
We now have the right length: $72 + 1 = 73$.
openssl asn1parse -i -in ecTest.csr -strparse <offset of the signature>
to show its structure. $\endgroup$ – Marc Sep 16 '20 at 5:10r
ands
each is 33 bytes instead of 32, why is that? $\endgroup$ – porente Sep 16 '20 at 5:19