Document ASN.1 printing functions.

ASN1_STRING_print_ex is extremely complex and attempting to implement
RFC2253, so write some tests for it. Along the way, unexport
CHARTYPE_*, which are internal book-keeping used in
ASN1_STRING_print_ex.

Change-Id: Idb27cd40fb66dc099d1fd6d039a00404608c2063
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48776
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c
index 2394939..8cf4398 100644
--- a/crypto/asn1/a_strex.c
+++ b/crypto/asn1/a_strex.c
@@ -67,6 +67,15 @@
 #include "internal.h"
 
 
+// These flags must be distinct from |ESC_FLAGS| and fit in a byte.
+
+// Character is a valid PrintableString character
+#define CHARTYPE_PRINTABLESTRING 0x10
+// Character needs escaping if it is the first character
+#define CHARTYPE_FIRST_ESC_2253 0x20
+// Character needs escaping if it is the last character
+#define CHARTYPE_LAST_ESC_2253 0x40
+
 #define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
 
 #define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
@@ -188,6 +197,8 @@
             orflags = CHARTYPE_FIRST_ESC_2253;
         else
             orflags = 0;
+        /* TODO(davidben): Replace this with |cbs_get_ucs2_be|, etc., to check
+         * for invalid codepoints. */
         switch (charwidth) {
         case 4:
             c = ((uint32_t)*p++) << 24;
diff --git a/crypto/asn1/asn1_par.c b/crypto/asn1/asn1_par.c
index b1a01ed..282ad23 100644
--- a/crypto/asn1/asn1_par.c
+++ b/crypto/asn1/asn1_par.c
@@ -72,7 +72,7 @@
     };
 
     if ((tag == V_ASN1_NEG_INTEGER) || (tag == V_ASN1_NEG_ENUMERATED))
-        tag &= ~0x100;
+        tag &= ~V_ASN1_NEG;
 
     if (tag < 0 || tag > 30)
         return "(unknown)";
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index bc6e3f7..0d9c43c 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -530,6 +530,269 @@
   }
 }
 
+static std::vector<uint8_t> StringToVector(const std::string &str) {
+  return std::vector<uint8_t>(str.begin(), str.end());
+}
+
+TEST(ASN1Test, StringPrintEx) {
+  const struct {
+    int type;
+    std::vector<uint8_t> data;
+    int str_flags;
+    unsigned long flags;
+    std::string expected;
+  } kTests[] = {
+      // A string like "hello" is never escaped or quoted.
+      // |ASN1_STRFLGS_ESC_QUOTE| only introduces quotes when needed. Note
+      // OpenSSL interprets T61String as Latin-1.
+      {V_ASN1_T61STRING, StringToVector("hello"), 0, 0, "hello"},
+      {V_ASN1_T61STRING, StringToVector("hello"), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB,
+       "hello"},
+      {V_ASN1_T61STRING, StringToVector("hello"), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
+           ASN1_STRFLGS_ESC_QUOTE,
+       "hello"},
+
+      // By default, 8-bit characters are printed without escaping.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       0,
+       std::string(1, '\0') + "\n\x80\xff,+\"\\<>;"},
+
+      // Flags control different escapes. Note that any escape flag will cause
+      // blackslashes to be escaped.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_2253,
+       std::string(1, '\0') + "\n\x80\xff\\,\\+\\\"\\\\\\<\\>\\;"},
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_CTRL,
+       "\\00\\0A\x80\xff,+\"\\\\<>;"},
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_MSB,
+       std::string(1, '\0') + "\n\\80\\FF,+\"\\\\<>;"},
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB,
+       "\\00\\0A\\80\\FF\\,\\+\\\"\\\\\\<\\>\\;"},
+
+      // When quoted, fewer characters need to be escaped in RFC2253.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
+           ASN1_STRFLGS_ESC_QUOTE,
+       "\"\\00\\0A\\80\\FF,+\\\"\\\\<>;\""},
+
+      // If no characters benefit from quotes, no quotes are added.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, '"', '\\'},
+       0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
+           ASN1_STRFLGS_ESC_QUOTE,
+       "\\00\\0A\\80\\FF\\\"\\\\"},
+
+      // RFC2253 only escapes spaces at the start and end of a string.
+      {V_ASN1_T61STRING, StringToVector("   "), 0, ASN1_STRFLGS_ESC_2253,
+       "\\  \\ "},
+      {V_ASN1_T61STRING, StringToVector("   "), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_QUOTE, "\"   \""},
+
+      // RFC2253 only escapes # at the start of a string.
+      {V_ASN1_T61STRING, StringToVector("###"), 0, ASN1_STRFLGS_ESC_2253,
+       "\\###"},
+      {V_ASN1_T61STRING, StringToVector("###"), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_QUOTE, "\"###\""},
+
+      // By default, strings are decoded and Unicode code points are
+      // individually escaped.
+      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
+       0, ASN1_STRFLGS_ESC_MSB, "a\\80\\U0100\\W00010000"},
+      {V_ASN1_BMPSTRING,
+       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB,
+       "a\\80\\U0100"},
+      {V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a',   //
+        0x00, 0x00, 0x00, 0x80,  //
+        0x00, 0x00, 0x01, 0x00,  //
+        0x00, 0x01, 0x00, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB,
+       "a\\80\\U0100\\W00010000"},
+
+      // |ASN1_STRFLGS_UTF8_CONVERT| normalizes everything to UTF-8 and then
+      // escapes individual bytes.
+      {V_ASN1_IA5STRING, StringToVector("a\x80"), 0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT, "a\\C2\\80"},
+      {V_ASN1_T61STRING, StringToVector("a\x80"), 0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT, "a\\C2\\80"},
+      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
+       0, ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
+       "a\\C2\\80\\C4\\80\\F0\\90\\80\\80"},
+      {V_ASN1_BMPSTRING,
+       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
+       "a\\C2\\80\\C4\\80"},
+      {V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a',   //
+        0x00, 0x00, 0x00, 0x80,  //
+        0x00, 0x00, 0x01, 0x00,  //
+        0x00, 0x01, 0x00, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
+       "a\\C2\\80\\C4\\80\\F0\\90\\80\\80"},
+
+      // The same as above, but without escaping the UTF-8 encoding.
+      {V_ASN1_IA5STRING, StringToVector("a\x80"), 0, ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80"},
+      {V_ASN1_T61STRING, StringToVector("a\x80"), 0, ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80"},
+      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
+       0, ASN1_STRFLGS_UTF8_CONVERT, "a\xc2\x80\xc4\x80\xf0\x90\x80\x80"},
+      {V_ASN1_BMPSTRING,
+       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
+       0,
+       ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80\xc4\x80"},
+      {V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a',   //
+        0x00, 0x00, 0x00, 0x80,  //
+        0x00, 0x00, 0x01, 0x00,  //
+        0x00, 0x01, 0x00, 0x00},
+       0,
+       ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80\xc4\x80\xf0\x90\x80\x80"},
+
+      // Types that cannot be decoded are, by default, treated as a byte string.
+      {V_ASN1_OCTET_STRING, {0xff}, 0, 0, "\xff"},
+      {-1, {0xff}, 0, 0, "\xff"},
+      {100, {0xff}, 0, 0, "\xff"},
+
+      // |ASN1_STRFLGS_UTF8_CONVERT| still converts these bytes to UTF-8.
+      //
+      // TODO(davidben): This seems like a bug. Although it's unclear because
+      // the non-RFC2253 options aren't especially sound. Can we just remove
+      // them?
+      {V_ASN1_OCTET_STRING, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
+      {-1, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
+      {100, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
+
+      // |ASN1_STRFLGS_IGNORE_TYPE| causes the string type to be ignored, so it
+      // is always treated as a byte string, even if it is not a valid encoding.
+      {V_ASN1_UTF8STRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
+      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
+      {V_ASN1_UNIVERSALSTRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
+
+      // |ASN1_STRFLGS_SHOW_TYPE| prepends the type name.
+      {V_ASN1_UTF8STRING, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "UTF8STRING:a"},
+      {-1, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "(unknown):a"},
+      {100, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "(unknown):a"},
+
+      // |ASN1_STRFLGS_DUMP_ALL| and |ASN1_STRFLGS_DUMP_UNKNOWN| cause
+      // non-string types to be printed in hex, though without the DER wrapper
+      // by default.
+      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_UNKNOWN, "\\U2603"},
+      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL, "#E29883"},
+      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_UNKNOWN, "#E29883"},
+      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL, "#E29883"},
+
+      // |ASN1_STRFLGS_DUMP_DER| includes the entire element.
+      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER, "#0C03E29883"},
+      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER, "#0403E29883"},
+      {V_ASN1_BIT_STRING,
+       {0x80},
+       ASN1_STRING_FLAG_BITS_LEFT | 4,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
+       "#03020480"},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.type);
+    SCOPED_TRACE(Bytes(t.data));
+    SCOPED_TRACE(t.str_flags);
+    SCOPED_TRACE(t.flags);
+
+    bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(t.type));
+    ASSERT_TRUE(ASN1_STRING_set(str.get(), t.data.data(), t.data.size()));
+    str->flags = t.str_flags;
+
+    // If the |BIO| is null, it should measure the size.
+    int len = ASN1_STRING_print_ex(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
+
+    // Measuring the size should also work for the |FILE| version
+    len = ASN1_STRING_print_ex_fp(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
+
+    // Actually print the string.
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(bio);
+    len = ASN1_STRING_print_ex(bio.get(), str.get(), t.flags);
+    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
+
+    const uint8_t *bio_contents;
+    size_t bio_len;
+    ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_contents, &bio_len));
+    EXPECT_EQ(t.expected, std::string(bio_contents, bio_contents + bio_len));
+  }
+
+  const struct {
+    int type;
+    std::vector<uint8_t> data;
+    int str_flags;
+    unsigned long flags;
+  } kUnprintableTests[] = {
+      // When decoding strings, invalid codepoints are errors.
+      {V_ASN1_UTF8STRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+      {V_ASN1_UNIVERSALSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+  };
+  for (const auto &t : kUnprintableTests) {
+    SCOPED_TRACE(t.type);
+    SCOPED_TRACE(Bytes(t.data));
+    SCOPED_TRACE(t.str_flags);
+    SCOPED_TRACE(t.flags);
+
+    bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(t.type));
+    ASSERT_TRUE(ASN1_STRING_set(str.get(), t.data.data(), t.data.size()));
+    str->flags = t.str_flags;
+
+    // If the |BIO| is null, it should measure the size.
+    int len = ASN1_STRING_print_ex(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, -1);
+    ERR_clear_error();
+
+    // Measuring the size should also work for the |FILE| version
+    len = ASN1_STRING_print_ex_fp(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, -1);
+    ERR_clear_error();
+
+    // Actually print the string.
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(bio);
+    len = ASN1_STRING_print_ex(bio.get(), str.get(), t.flags);
+    EXPECT_EQ(len, -1);
+    ERR_clear_error();
+  }
+}
+
 // The ASN.1 macros do not work on Windows shared library builds, where usage of
 // |OPENSSL_EXPORT| is a bit stricter.
 #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY)
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h
index 8bbb49b..4cf71ad 100644
--- a/include/openssl/asn1.h
+++ b/include/openssl/asn1.h
@@ -178,6 +178,10 @@
 #define B_ASN1_GENERALIZEDTIME 0x8000
 #define B_ASN1_SEQUENCE 0x10000
 
+// ASN1_tag2str returns a string representation of |tag|, interpret as a tag
+// number for a universal type, or |V_ASN1_NEG_*|.
+OPENSSL_EXPORT const char *ASN1_tag2str(int tag);
+
 
 // Strings.
 //
@@ -740,6 +744,99 @@
 // the macros, document them, and move them to this section.
 
 
+// Human-readable output.
+//
+// The following functions output types in some human-readable format. These
+// functions may be used for debugging and logging. However, the output should
+// not be consumed programmatically. They may be ambiguous or lose information.
+
+// ASN1_UTCTIME_print writes a human-readable representation of |a| to |out|. It
+// returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *out, const ASN1_UTCTIME *a);
+
+// ASN1_GENERALIZEDTIME_print writes a human-readable representation of |a| to
+// |out|. It returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *out,
+                                              const ASN1_GENERALIZEDTIME *a);
+
+// ASN1_TIME_print writes a human-readable representation of |a| to |out|. It
+// returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_TIME_print(BIO *out, const ASN1_TIME *a);
+
+// ASN1_STRING_print writes a human-readable representation of |str| to |out|.
+// It returns one on success and zero on error. Unprintable characters are
+// replaced with '.'.
+OPENSSL_EXPORT int ASN1_STRING_print(BIO *out, const ASN1_STRING *str);
+
+// ASN1_STRFLGS_ESC_2253 causes characters to be escaped as in RFC2253, section
+// 2.4.
+#define ASN1_STRFLGS_ESC_2253 1
+
+// ASN1_STRFLGS_ESC_CTRL causes all control characters to be escaped.
+#define ASN1_STRFLGS_ESC_CTRL 2
+
+// ASN1_STRFLGS_ESC_MSB causes all characters above 127 to be escaped.
+#define ASN1_STRFLGS_ESC_MSB 4
+
+// ASN1_STRFLGS_ESC_QUOTE causes the string to be surrounded by quotes, rather
+// than using backslashes, when characters are escaped. Fewer characters will
+// require escapes in this case.
+#define ASN1_STRFLGS_ESC_QUOTE 8
+
+// ASN1_STRFLGS_UTF8_CONVERT causes the string to be encoded as UTF-8, with each
+// byte in the UTF-8 encoding treated as an individual character for purposes of
+// escape sequences. If not set, each Unicode codepoint in the string is treated
+// as a character, with wide characters escaped as "\Uxxxx" or "\Wxxxxxxxx".
+// Note this can be ambiguous if |ASN1_STRFLGS_ESC_*| are all unset. In that
+// case, backslashes are not escaped, but wide characters are.
+#define ASN1_STRFLGS_UTF8_CONVERT 0x10
+
+// ASN1_STRFLGS_IGNORE_TYPE causes the string type to be ignored. The
+// |ASN1_STRING| in-memory representation will be printed directly.
+#define ASN1_STRFLGS_IGNORE_TYPE 0x20
+
+// ASN1_STRFLGS_SHOW_TYPE causes the string type to be included in the output.
+#define ASN1_STRFLGS_SHOW_TYPE 0x40
+
+// ASN1_STRFLGS_DUMP_ALL causes all strings to be printed as a hexdump, using
+// RFC2253 hexstring notation, such as "#0123456789ABCDEF".
+#define ASN1_STRFLGS_DUMP_ALL 0x80
+
+// ASN1_STRFLGS_DUMP_UNKNOWN behaves like |ASN1_STRFLGS_DUMP_ALL| but only
+// applies to values of unknown type. If unset, unknown values will print
+// their contents as single-byte characters with escape sequences.
+#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100
+
+// ASN1_STRFLGS_DUMP_DER causes hexdumped strings (as determined by
+// |ASN1_STRFLGS_DUMP_ALL| or |ASN1_STRFLGS_DUMP_UNKNOWN|) to print the entire
+// DER element as in RFC2253, rather than only the contents of the
+// |ASN1_STRING|.
+#define ASN1_STRFLGS_DUMP_DER 0x200
+
+// ASN1_STRFLGS_RFC2253 causes the string to be escaped as in RFC2253,
+// additionally escaping control characters.
+#define ASN1_STRFLGS_RFC2253                                              \
+  (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | \
+   ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN |                \
+   ASN1_STRFLGS_DUMP_DER)
+
+// ASN1_STRING_print_ex writes a human-readable representation of |str| to
+// |out|. It returns the number of bytes written on success and -1 on error. If
+// |out| is NULL, it returns the number of bytes it would have written, without
+// writing anything.
+//
+// The |flags| should be a combination of combination of |ASN1_STRFLGS_*|
+// constants. See the documentation for each flag for how it controls the
+// output. If unsure, use |ASN1_STRFLGS_RFC2253|.
+OPENSSL_EXPORT int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str,
+                                        unsigned long flags);
+
+// ASN1_STRING_print_ex_fp behaves like |ASN1_STRING_print_ex| but writes to a
+// |FILE| rather than a |BIO|.
+OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
+                                           unsigned long flags);
+
+
 // Underdocumented functions.
 //
 // The following functions are not yet documented and organized.
@@ -876,75 +973,6 @@
 
 #define DECLARE_ASN1_ITEM(name) extern OPENSSL_EXPORT const ASN1_ITEM name##_it;
 
-// Parameters used by ASN1_STRING_print_ex()
-
-// These determine which characters to escape:
-// RFC2253 special characters, control characters and
-// MSB set characters
-
-#define ASN1_STRFLGS_ESC_2253 1
-#define ASN1_STRFLGS_ESC_CTRL 2
-#define ASN1_STRFLGS_ESC_MSB 4
-
-
-// This flag determines how we do escaping: normally
-// RC2253 backslash only, set this to use backslash and
-// quote.
-
-#define ASN1_STRFLGS_ESC_QUOTE 8
-
-
-// These three flags are internal use only.
-
-// Character is a valid PrintableString character
-#define CHARTYPE_PRINTABLESTRING 0x10
-// Character needs escaping if it is the first character
-#define CHARTYPE_FIRST_ESC_2253 0x20
-// Character needs escaping if it is the last character
-#define CHARTYPE_LAST_ESC_2253 0x40
-
-// NB the internal flags are safely reused below by flags
-// handled at the top level.
-
-// If this is set we convert all character strings
-// to UTF8 first
-
-#define ASN1_STRFLGS_UTF8_CONVERT 0x10
-
-// If this is set we don't attempt to interpret content:
-// just assume all strings are 1 byte per character. This
-// will produce some pretty odd looking output!
-
-#define ASN1_STRFLGS_IGNORE_TYPE 0x20
-
-// If this is set we include the string type in the output
-#define ASN1_STRFLGS_SHOW_TYPE 0x40
-
-// This determines which strings to display and which to
-// 'dump' (hex dump of content octets or DER encoding). We can
-// only dump non character strings or everything. If we
-// don't dump 'unknown' they are interpreted as character
-// strings with 1 octet per character and are subject to
-// the usual escaping options.
-
-#define ASN1_STRFLGS_DUMP_ALL 0x80
-#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100
-
-// These determine what 'dumping' does, we can dump the
-// content octets or the DER encoding: both use the
-// RFC2253 #XXXXX notation.
-
-#define ASN1_STRFLGS_DUMP_DER 0x200
-
-// All the string flags consistent with RFC2253,
-// escaping control characters isn't essential in
-// RFC2253 but it is advisable anyway.
-
-#define ASN1_STRFLGS_RFC2253                                              \
-  (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | \
-   ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN |                \
-   ASN1_STRFLGS_DUMP_DER)
-
 DEFINE_STACK_OF(ASN1_INTEGER)
 
 DEFINE_STACK_OF(ASN1_TYPE)
@@ -1106,19 +1134,9 @@
 
 OPENSSL_EXPORT void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x);
 OPENSSL_EXPORT int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x);
-OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
-                                           unsigned long flags);
 
 OPENSSL_EXPORT void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
 OPENSSL_EXPORT int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x);
-OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *fp,
-                                              const ASN1_GENERALIZEDTIME *a);
-OPENSSL_EXPORT int ASN1_TIME_print(BIO *fp, const ASN1_TIME *a);
-OPENSSL_EXPORT int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v);
-OPENSSL_EXPORT int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str,
-                                        unsigned long flags);
-OPENSSL_EXPORT const char *ASN1_tag2str(int tag);
 
 // Used to load and write netscape format cert