gbarr/perl-Convert-ASN1

Debug output using print instead of printf?

Closed this issue · 7 comments

There are some handy certs etc from RFC 3280 and RFC 5280 over at https://csrc.nist.gov/Projects/pki-testing/Sample-Certificates-and-CRLs

I'm using perl 5.34 and Convert::ASN1 0.330.

When I pass the example CRL into asn_dump() I see a few "%d" markers in the output. It looks like the asn_dump output format is changing a bit but there's maybe a print still being used instead of a printf. So it currently looks a bit busted:

005E   13:     [UNIVERSAL %d]: 23
0060     :       30 35 30 32 30 35 31 32 30 30 30 30 5A __ __ __ 050205120000Z
006D   13:     [UNIVERSAL %d]: 23
006F     :       30 35 30 32 30 36 31 32 30 30 30 30 5A __ __ __ 050206120000Z
007C   34:     SEQUENCE: 16 {
007E   32:       SEQUENCE: 16 {
0080    1:         INTEGER: 2 = 18
0083   13:         [UNIVERSAL %d]: 23
0085     :           30 34 31 31 31 39 31 35 35 37 30 33 5A __ __ __ 041119155703Z
0092   12:         SEQUENCE: 16 {
0094   10:           SEQUENCE: 16 {
0096    3:             OBJECT ID: 6 = 2.5.29.21
009B    3:             STRING: 4
009D     :               0A 01 01 __ __ __ __ __ __ __ __ __ __ __ __ __ ...
00A0     :           }
00A0     :         }
00A0     :       }
00A0     :     }
00A0   47:     [CONTEXT %d]: 0 {

This should resolve your issue. can you confirm?

diff --git a/lib/Convert/ASN1/Debug.pm b/lib/Convert/ASN1/Debug.pm
index d877b09..c706bbe 100644
--- a/lib/Convert/ASN1/Debug.pm
+++ b/lib/Convert/ASN1/Debug.pm
@@ -95,7 +95,7 @@ sub asn_dump {
     my $label = $type{sprintf("%02X",$tag & ~0x20)}
                || $type{sprintf("%02X",$tag & 0xC0)}
                || "[UNIVERSAL %d]";
-    print "$label: $tnum";
+    printf "$label: $tnum";
 
     if ($tag & ASN_CONSTRUCTOR) {
       print " {\n";

Hi, no that results in printf being called with too few arguments in many cases. e.g.

0000  352: SEQUENCE: 16 {
0004  202:   SEQUENCE: 16 {
0007    1:     INTEGER: 2 = 1
000A   13:     SEQUENCE: 16 {
000C    9:       OBJECT ID: 6 = 1.2.840.113549.1.1.5
0017    0:       NULL: 5
0019     :     }
0019   67:     SEQUENCE: 16 {
001B   19:       SET: 17 {
001D   17:         SEQUENCE: 16 {
001F   10:           OBJECT ID: 6 = 0.9.2342.19200300.100.1.25
Missing argument in printf at /Users/cjr/lib/perl5/Convert/ASN1/Debug.pm line 98, <> line 1.
 at /Users/cjr/lib/perl5/Convert/ASN1/Debug.pm line 98, <ARGV> line 1.
	Convert::ASN1::asn_dump("*main::STDOUT", "0\x{82}\x{1}`0\x{81}\x{ca}\x{2}\x{1}\x{1}0\x{d}\x{6}\x{9}*\x{86}H\x{86}\x{f7}\x{d}\x{1}\x{1}\x{5}\x{5}\x{0}0C1\x{13}0\x{11}\x{6}\x{a}\x{9}\x{92}&\x{89}\x{93}\x{f2},d\x{1}\x{19}\x{16}\x{3}com1\x{17}0\x{15}\x{6}\x{a}\x{9}\x{92}&\x{89}\x{93}\x{f2},"...) called at /Users/cjr/bin/dumpber line 63
002B    3:           [UNIVERSAL 0]: 22
002D     :             63 6F 6D __ __ __ __ __ __ __ __ __ __ __ __ __ com
0030     :         }
0030     :       }

Having things in %type with optional format strings seems the cause of the issue. Changing to something like

$ diff -bu ~/Downloads/Debug.pm ~/lib/perl5/Convert/ASN1/Debug.pm
--- /Users/cjr/Downloads/Debug.pm	2023-08-02 09:17:17
+++ /Users/cjr/lib/perl5/Convert/ASN1/Debug.pm	2023-08-02 10:04:18
@@ -55,6 +55,7 @@
       05	NULL
       06	OBJECT ID
       80	[CONTEXT %d]
+      00	[UNIVERSAL %d]
     )
   )
 );
@@ -93,9 +94,9 @@
     printf $fmt. " %4d: %s",$start,$len,$indent;
 
     my $label = $type{sprintf("%02X",$tag & ~0x20)}
-		|| $type{sprintf("%02X",$tag & 0xC0)}
-		|| "[UNIVERSAL %d]";
-    printf "$label: $tnum";
+		|| $type{sprintf("%02X",$tag & 0xC0)};
+    $label = sprintf($label, $tag & ~0xE0) if $label =~ /%d/;
+    print "$label: $tnum";
 
     if ($tag & ASN_CONSTRUCTOR) {
       print " {\n";

works for me. It gets rid of the special case || "[UNIVERSAL %d]"and handles all the other tag classes. (Note the "[CONTEXT %d]" at the end of my original error report.) Checking for a %d in the label does feel a bit tacky.

Here's an alternative, which moves all the tag classes into %classes so we can reliably use sprintf:

$ diff -bu ~/Downloads/Debug.pm ~/lib/perl5/Convert/ASN1/Debug.pm
--- /Users/cjr/Downloads/Debug.pm	2023-08-02 09:17:17
+++ /Users/cjr/lib/perl5/Convert/ASN1/Debug.pm	2023-08-02 10:16:36
@@ -49,12 +49,19 @@
       11	SET
       02	INTEGER
       03	BIT STRING
-      C0	[PRIVATE %d]
       04	STRING
-      40	[APPLICATION %d]
       05	NULL
       06	OBJECT ID
+    )
+  )
+);
+
+my %classes = (
+  split(/[\t\n]\s*/,
+    q(C0	[PRIVATE %d]
+      40	[APPLICATION %d]
       80	[CONTEXT %d]
+      00	[UNIVERSAL %d]
     )
   )
 );
@@ -93,9 +100,8 @@
     printf $fmt. " %4d: %s",$start,$len,$indent;
 
     my $label = $type{sprintf("%02X",$tag & ~0x20)}
-		|| $type{sprintf("%02X",$tag & 0xC0)}
-		|| "[UNIVERSAL %d]";
-    printf "$label: $tnum";
+		|| sprintf($classes{sprintf("%02X",$tag & 0xC0)}, $tag & ~0xE0);
+    print "$label: $tnum";
 
     if ($tag & ASN_CONSTRUCTOR) {
       print " {\n";

Cleaner? Dunno! Both work.

Sure, I'm attaching the script I've been using. I just used it with https://csrc.nist.gov/CSRC/media/Projects/PKI-Testing/documents/pkixtools/rfc5280_CRL.crl

$ dumpber rfc5280_CRL.crl

dumpber.zip

Take a look at this addition. It attempts to make the code for the tag to be show the leading zero when necessary. The tag for integer for instance is 06

diff --git a/lib/Convert/ASN1/Debug.pm b/lib/Convert/ASN1/Debug.pm
index ec68b80..f77b092 100644
--- a/lib/Convert/ASN1/Debug.pm
+++ b/lib/Convert/ASN1/Debug.pm
@@ -101,7 +101,7 @@ sub asn_dump {
 
     my $label = $type{sprintf("%02X",$tag & ~0x20)}
                || sprintf($classes{sprintf("%02X",$tag & 0xC0)}, $tag & ~0xE0);
-    print "$label: $tnum";
+    printf ("$label: %02d", $tnum);
 
     if ($tag & ASN_CONSTRUCTOR) {
       print " {\n";

BER can have tags bigger than one byte (the asn_decode_tag and asn_decode_tag2 functions will decode these). I was worried that "%02d" would truncate these but although it doesn't, including the leading zero for short tag numbers looks odd to me and might mislead readers into assuming hex.

I think keeping it as print "$label: $tnum"; is fine.

# Just checking...
printf("%02d", 3) -> "03"
printf("%02d", 30) -> "30"
printf("%02d", 999) -> "999"

Here's an example of a real life certificate extension using large tag numbers: https://developer.android.com/training/articles/security-key-attestation#certificate_schema