Repository: randombit/botan
Pull request #2552 (2020-12-15 13:50:42)

Convert ASN1_Tag to an enum class

codecov-io: # [Codecov](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=h1) Report
> Merging [#2552](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=desc) (29c762d) into [master](https://codecov.io/gh/randombit/botan/commit/972266811936d5e32860302542bfadc1c0d3e553?el=desc) (9722668) will **decrease** coverage by `0.01%`.
> The diff coverage is `95.84%`.

[![Impacted file tree graph](https://codecov.io/gh/randombit/botan/pull/2552/graphs/tree.svg?width=650&height=150&src=pr&token=VR6iYmSPH8)](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2552      +/-   ##
==========================================
- Coverage   92.40%   92.38%   -0.02%     
==========================================
  Files         564      564              
  Lines       62586    62590       +4     
  Branches     6150     6150              
==========================================
- Hits        57830    57822       -8     
- Misses       4724     4736      +12     
  Partials       32       32              
```


| [Impacted Files](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/lib/prov/tpm/tpm.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9wcm92L3RwbS90cG0uY3Bw) | `5.26% <0.00%> (ø)` | |
| [src/lib/rng/system\_rng/system\_rng.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9ybmcvc3lzdGVtX3JuZy9zeXN0ZW1fcm5nLmNwcA==) | `75.55% <ø> (ø)` | |
| [src/lib/pubkey/ed25519/ed25519\_key.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9wdWJrZXkvZWQyNTUxOS9lZDI1NTE5X2tleS5jcHA=) | `88.61% <50.00%> (ø)` | |
| [src/lib/asn1/asn1\_obj.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9hc24xL2FzbjFfb2JqLmNwcA==) | `90.67% <84.21%> (-1.57%)` | :arrow_down: |
| [src/lib/x509/x509\_ext.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi94NTA5L3g1MDlfZXh0LmNwcA==) | `86.63% <85.00%> (ø)` | |
| [src/lib/asn1/ber\_dec.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9hc24xL2Jlcl9kZWMuY3Bw) | `94.23% <90.00%> (ø)` | |
| [src/lib/asn1/asn1\_str.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9hc24xL2FzbjFfc3RyLmNwcA==) | `97.50% <92.85%> (ø)` | |
| [src/lib/asn1/der\_enc.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9hc24xL2Rlcl9lbmMuY3Bw) | `83.22% <93.33%> (+0.21%)` | :arrow_up: |
| [src/cli/pk\_crypt.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2NsaS9wa19jcnlwdC5jcHA=) | `81.08% <100.00%> (ø)` | |
| [src/lib/asn1/alg\_id.cpp](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree#diff-c3JjL2xpYi9hc24xL2FsZ19pZC5jcHA=) | `100.00% <100.00%> (ø)` | |
| ... and [49 more](https://codecov.io/gh/randombit/botan/pull/2552/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=footer). Last update [9722668...29c762d](https://codecov.io/gh/randombit/botan/pull/2552?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/doc/api_ref/pkcs11.rst b/doc/api_ref/pkcs11.rst
index 498f11b0d..4e7886168 100644
--- a/doc/api_ref/pkcs11.rst
+++ b/doc/api_ref/pkcs11.rst
@@ -868,7 +868,7 @@ Code example:
       // import to card
       Botan::PKCS11::EC_PublicKeyImportProperties pub_import_props( priv_key_sw.DER_domain(),
          Botan::DER_Encoder().encode(priv_key_sw.public_point().encode(Botan::PointGFp::Compression_Type::UNCOMPRESSED ),
-         Botan::OCTET_STRING ).get_contents_unlocked() );
+         Botan::ASN1_Tag::OCTET_STRING ).get_contents_unlocked() );
 
       pub_import_props.set_token( true );
       pub_import_props.set_verify( true );
@@ -1020,7 +1020,7 @@ Code example:
       Botan::PKCS11::EC_PublicKeyImportProperties pub_import_props( priv_key_sw.DER_domain(),
          Botan::DER_Encoder().encode(
            priv_key_sw.public_point().encode(Botan::PointGFp::Compression_Type::UNCOMPRESSED ),
-           Botan::OCTET_STRING ).get_contents_unlocked() );
+           Botan::ASN1_Tag::OCTET_STRING ).get_contents_unlocked() );
 
       pub_import_props.set_token( true );
       pub_import_props.set_private( false );
diff --git a/src/cli/pk_crypt.cpp b/src/cli/pk_crypt.cpp
index 111a60129..c30b17b48 100644
--- a/src/cli/pk_crypt.cpp
+++ b/src/cli/pk_crypt.cpp
@@ -89,12 +89,12 @@ class PK_Encrypt final : public Command
          std::vector<uint8_t> buf;
          Botan::DER_Encoder der(buf);
 
-         der.start_cons(Botan::SEQUENCE)
+         der.start_sequence()
             .encode(pk_alg_id)
-            .encode(encrypted_key, Botan::OCTET_STRING)
+            .encode(encrypted_key, Botan::ASN1_Tag::OCTET_STRING)
             .encode(aead_oid)
-            .encode(nonce, Botan::OCTET_STRING)
-            .encode(data, Botan::OCTET_STRING)
+            .encode(nonce, Botan::ASN1_Tag::OCTET_STRING)
+            .encode(data, Botan::ASN1_Tag::OCTET_STRING)
             .end_cons();
 
          output() << Botan::PEM_Code::encode(buf, "PUBKEY ENCRYPTED MESSAGE", 72);
@@ -145,12 +145,12 @@ class PK_Decrypt final : public Command
             Botan::DataSource_Stream input(get_arg("datafile"));
 
             Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE"))
-               .start_cons(Botan::SEQUENCE)
+               .start_sequence()
                   .decode(pk_alg_id)
-                  .decode(encrypted_key, Botan::OCTET_STRING)
+                  .decode(encrypted_key, Botan::ASN1_Tag::OCTET_STRING)
                   .decode(aead_oid)
-                  .decode(nonce, Botan::OCTET_STRING)
-                  .decode(data, Botan::OCTET_STRING)
+                  .decode(nonce, Botan::ASN1_Tag::OCTET_STRING)
+                  .decode(data, Botan::ASN1_Tag::OCTET_STRING)
                .end_cons();
             }
          catch(Botan::Decoding_Error&)
diff --git a/src/lib/asn1/alg_id.cpp b/src/lib/asn1/alg_id.cpp
index b4de05418..2f0848132 100644
--- a/src/lib/asn1/alg_id.cpp
+++ b/src/lib/asn1/alg_id.cpp
@@ -89,7 +89,7 @@ bool operator!=(const AlgorithmIdentifier& a1, const AlgorithmIdentifier& a2)
 */
 void AlgorithmIdentifier::encode_into(DER_Encoder& codec) const
    {
-   codec.start_cons(SEQUENCE)
+   codec.start_sequence()
       .encode(oid())
       .raw_bytes(parameters())
    .end_cons();
@@ -100,7 +100,7 @@ void AlgorithmIdentifier::encode_into(DER_Encoder& codec) const
 */
 void AlgorithmIdentifier::decode_from(BER_Decoder& codec)
    {
-   codec.start_cons(SEQUENCE)
+   codec.start_sequence()
       .decode(m_oid)
       .raw_bytes(m_parameters)
    .end_cons();
diff --git a/src/lib/asn1/asn1_obj.cpp b/src/lib/asn1/asn1_obj.cpp
index 0db10b4ce..4a471ba50 100644
--- a/src/lib/asn1/asn1_obj.cpp
+++ b/src/lib/asn1/asn1_obj.cpp
@@ -33,19 +33,19 @@ void BER_Object::assert_is_a(ASN1_Tag expected_type_tag, ASN1_Tag expected_class
 
       msg << "Tag mismatch when decoding " << descr << " got ";
 
-      if(m_class_tag == NO_OBJECT && m_type_tag == NO_OBJECT)
+      if(m_class_tag == ASN1_Tag::NO_OBJECT && m_type_tag == ASN1_Tag::NO_OBJECT)
          {
          msg << "EOF";
          }
       else
          {
-         if(m_class_tag == UNIVERSAL || m_class_tag == CONSTRUCTED)
+         if(m_class_tag == ASN1_Tag::UNIVERSAL || m_class_tag == ASN1_Tag::CONSTRUCTED)
             {
             msg << asn1_tag_to_string(m_type_tag);
             }
          else
             {
-            msg << std::to_string(m_type_tag);
+            msg << std::to_string(static_cast<uint32_t>(m_type_tag));
             }
 
          msg << "/" << asn1_class_to_string(m_class_tag);
@@ -53,13 +53,13 @@ void BER_Object::assert_is_a(ASN1_Tag expected_type_tag, ASN1_Tag expected_class
 
       msg << " expected ";
 
-      if(expected_class_tag == UNIVERSAL || expected_class_tag == CONSTRUCTED)
+      if(expected_class_tag == ASN1_Tag::UNIVERSAL || expected_class_tag == ASN1_Tag::CONSTRUCTED)
          {
          msg << asn1_tag_to_string(expected_type_tag);
          }
       else
          {
-         msg << std::to_string(expected_type_tag);
+         msg << std::to_string(static_cast<uint32_t>(expected_type_tag));
          }
 
       msg << "/" << asn1_class_to_string(expected_class_tag);
@@ -88,17 +88,17 @@ std::string asn1_class_to_string(ASN1_Tag type)
    {
    switch(type)
       {
-      case UNIVERSAL:
+      case ASN1_Tag::UNIVERSAL:
          return "UNIVERSAL";
-      case CONSTRUCTED:
+      case ASN1_Tag::CONSTRUCTED:
          return "CONSTRUCTED";
-      case CONTEXT_SPECIFIC:
+      case ASN1_Tag::CONTEXT_SPECIFIC:
          return "CONTEXT_SPECIFIC";
-      case APPLICATION:
+      case ASN1_Tag::APPLICATION:
          return "APPLICATION";
-      case CONSTRUCTED | CONTEXT_SPECIFIC:
+      case ASN1_Tag::PRIVATE:
          return "PRIVATE";
-      case Botan::NO_OBJECT:
+      case ASN1_Tag::NO_OBJECT:
          return "NO_OBJECT";
       default:
          return "CLASS(" + std::to_string(static_cast<size_t>(type)) + ")";
@@ -109,68 +109,68 @@ std::string asn1_tag_to_string(ASN1_Tag type)
    {
    switch(type)
       {
-      case Botan::SEQUENCE:
+      case ASN1_Tag::SEQUENCE:
          return "SEQUENCE";
 
-      case Botan::SET:
+      case ASN1_Tag::SET:
          return "SET";
 
-      case Botan::PRINTABLE_STRING:
+      case ASN1_Tag::PRINTABLE_STRING:
          return "PRINTABLE STRING";
 
-      case Botan::NUMERIC_STRING:
+      case ASN1_Tag::NUMERIC_STRING:
          return "NUMERIC STRING";
 
-      case Botan::IA5_STRING:
+      case ASN1_Tag::IA5_STRING:
          return "IA5 STRING";
 
-      case Botan::T61_STRING:
+      case ASN1_Tag::T61_STRING:
          return "T61 STRING";
 
-      case Botan::UTF8_STRING:
+      case ASN1_Tag::UTF8_STRING:
          return "UTF8 STRING";
 
-      case Botan::VISIBLE_STRING:
+      case ASN1_Tag::VISIBLE_STRING:
          return "VISIBLE STRING";
 
-      case Botan::BMP_STRING:
+      case ASN1_Tag::BMP_STRING:
          return "BMP STRING";
 
-      case Botan::UNIVERSAL_STRING:
+      case ASN1_Tag::UNIVERSAL_STRING:
          return "UNIVERSAL STRING";
 
-      case Botan::UTC_TIME:
+      case ASN1_Tag::UTC_TIME:
          return "UTC TIME";
 
-      case Botan::GENERALIZED_TIME:
+      case ASN1_Tag::GENERALIZED_TIME:
          return "GENERALIZED TIME";
 
-      case Botan::OCTET_STRING:
+      case ASN1_Tag::OCTET_STRING:
          return "OCTET STRING";
 
-      case Botan::BIT_STRING:
+      case ASN1_Tag::BIT_STRING:
          return "BIT STRING";
 
-      case Botan::ENUMERATED:
+      case ASN1_Tag::ENUMERATED:
          return "ENUMERATED";
 
-      case Botan::INTEGER:
+      case ASN1_Tag::INTEGER:
          return "INTEGER";
 
-      case Botan::NULL_TAG:
+      case ASN1_Tag::NULL_TAG:
          return "NULL";
 
-      case Botan::OBJECT_ID:
+      case ASN1_Tag::OBJECT_ID:
          return "OBJECT";
 
-      case Botan::BOOLEAN:
+      case ASN1_Tag::BOOLEAN:
          return "BOOLEAN";
 
-      case Botan::NO_OBJECT:
+      case ASN1_Tag::NO_OBJECT:
          return "NO_OBJECT";
 
       default:
-         return "TAG(" + std::to_string(static_cast<size_t>(type)) + ")";
+         return "TAG(" + std::to_string(static_cast<uint32_t>(type)) + ")";
       }
    }
 
@@ -181,11 +181,13 @@ BER_Decoding_Error::BER_Decoding_Error(const std::string& str) :
    Decoding_Error("BER: " + str) {}
 
 BER_Bad_Tag::BER_Bad_Tag(const std::string& str, ASN1_Tag tag) :
-      BER_Decoding_Error(str + ": " + std::to_string(tag)) {}
+   BER_Decoding_Error(str + ": " + std::to_string(static_cast<uint32_t>(tag))) {}
 
 BER_Bad_Tag::BER_Bad_Tag(const std::string& str,
                          ASN1_Tag tag1, ASN1_Tag tag2) :
-   BER_Decoding_Error(str + ": " + std::to_string(tag1) + "/" + std::to_string(tag2)) {}
+   BER_Decoding_Error(str + ": " +
+                      std::to_string(static_cast<uint32_t>(tag1)) + "/" +
+                      std::to_string(static_cast<uint32_t>(tag2))) {}
 
 namespace ASN1 {
 
@@ -201,7 +203,7 @@ std::vector<uint8_t> put_in_sequence(const uint8_t bits[], size_t len)
    {
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .raw_bytes(bits, len)
       .end_cons();
    return output;
@@ -228,7 +230,8 @@ bool maybe_BER(DataSource& source)
       throw Stream_IO_Error("ASN1::maybe_BER: Source was empty");
       }
 
-   if(first_u8 == (SEQUENCE | CONSTRUCTED))
+   const auto cons_seq = ASN1_Tag::CONSTRUCTED | ASN1_Tag::SEQUENCE;
+   if(first_u8 == static_cast<uint8_t>(cons_seq))
       return true;
    return false;
    }
diff --git a/src/lib/asn1/asn1_obj.h b/src/lib/asn1/asn1_obj.h
index 3940ebe82..1538bd196 100644
--- a/src/lib/asn1/asn1_obj.h
+++ b/src/lib/asn1/asn1_obj.h
@@ -20,9 +20,8 @@ class DER_Encoder;
 
 /**
 * ASN.1 Type and Class Tags
-* This will become an enum class in a future major release
 */
-enum ASN1_Tag : uint32_t {
+enum class ASN1_Tag : uint32_t {
    UNIVERSAL        = 0x00,
    APPLICATION      = 0x40,
    CONTEXT_SPECIFIC = 0x80,
@@ -59,6 +58,16 @@ enum ASN1_Tag : uint32_t {
    DIRECTORY_STRING = 0xFF01
 };
 
+inline bool intersects(ASN1_Tag x, ASN1_Tag y)
+   {
+   return static_cast<uint32_t>(x) & static_cast<uint32_t>(y);
+   }
+
+inline ASN1_Tag operator|(ASN1_Tag x, ASN1_Tag y)
+   {
+   return static_cast<ASN1_Tag>(static_cast<uint32_t>(x) | static_cast<uint32_t>(y));
+   }
+
 std::string BOTAN_UNSTABLE_API asn1_tag_to_string(ASN1_Tag type);
 std::string BOTAN_UNSTABLE_API asn1_class_to_string(ASN1_Tag type);
 
@@ -99,7 +108,7 @@ class BOTAN_PUBLIC_API(2,0) ASN1_Object
 class BOTAN_PUBLIC_API(2,0) BER_Object final
    {
    public:
-      BER_Object() : m_type_tag(NO_OBJECT), m_class_tag(UNIVERSAL) {}
+      BER_Object() : m_type_tag(ASN1_Tag::NO_OBJECT), m_class_tag(ASN1_Tag::UNIVERSAL) {}
 
       BER_Object(const BER_Object& other) = default;
 
@@ -109,9 +118,9 @@ class BOTAN_PUBLIC_API(2,0) BER_Object final
 
       BER_Object& operator=(BER_Object&& other) = default;
 
-      bool is_set() const { return m_type_tag != NO_OBJECT; }
+      bool is_set() const { return m_type_tag != ASN1_Tag::NO_OBJECT; }
 
-      ASN1_Tag tagging() const { return ASN1_Tag(type() | get_class()); }
+      ASN1_Tag tagging() const { return type_tag() | class_tag(); }
 
       ASN1_Tag type_tag() const { return m_type_tag; }
       ASN1_Tag class_tag() const { return m_class_tag; }
@@ -341,7 +350,7 @@ class BOTAN_PUBLIC_API(2,0) ASN1_Time final : public ASN1_Object
       uint32_t m_hour = 0;
       uint32_t m_minute = 0;
       uint32_t m_second = 0;
-      ASN1_Tag m_tag = NO_OBJECT;
+      ASN1_Tag m_tag = ASN1_Tag::NO_OBJECT;
    };
 
 /*
diff --git a/src/lib/asn1/asn1_oid.cpp b/src/lib/asn1/asn1_oid.cpp
index 5466db05b..2839166da 100644
--- a/src/lib/asn1/asn1_oid.cpp
+++ b/src/lib/asn1/asn1_oid.cpp
@@ -168,7 +168,7 @@ void OID::encode_into(DER_Encoder& der) const
          encoding.push_back(m_id[i] & 0x7F);
          }
       }
-   der.add_object(OBJECT_ID, UNIVERSAL, encoding);
+   der.add_object(ASN1_Tag::OBJECT_ID, ASN1_Tag::UNIVERSAL, encoding);
    }
 
 /*
@@ -177,7 +177,7 @@ void OID::encode_into(DER_Encoder& der) const
 void OID::decode_from(BER_Decoder& decoder)
    {
    BER_Object obj = decoder.get_next_object();
-   if(obj.tagging() != OBJECT_ID)
+   if(obj.tagging() != ASN1_Tag::OBJECT_ID)
       throw BER_Bad_Tag("Error decoding OID, unknown tag", obj.tagging());
 
    const size_t length = obj.length();
diff --git a/src/lib/asn1/asn1_print.cpp b/src/lib/asn1/asn1_print.cpp
index faadad02b..5468f8bdd 100644
--- a/src/lib/asn1/asn1_print.cpp
+++ b/src/lib/asn1/asn1_print.cpp
@@ -90,7 +90,7 @@ void ASN1_Formatter::decode(std::ostream& output,
 
       BER_Decoder data(bits);
 
-      if(class_tag & CONSTRUCTED)
+      if(intersects(class_tag, ASN1_Tag::CONSTRUCTED))
          {
          BER_Decoder cons_info(obj.bits(), obj.length());
 
@@ -105,7 +105,7 @@ void ASN1_Formatter::decode(std::ostream& output,
                              format_bin(type_tag, class_tag, bits));
             }
          }
-      else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+      else if(intersects(class_tag, ASN1_Tag::APPLICATION) || intersects(class_tag, ASN1_Tag::CONTEXT_SPECIFIC))
          {
          bool success_parsing_cs = false;
 
@@ -142,7 +142,7 @@ void ASN1_Formatter::decode(std::ostream& output,
                              format_bin(type_tag, class_tag, bits));
             }
          }
-      else if(type_tag == OBJECT_ID)
+      else if(type_tag == ASN1_Tag::OBJECT_ID)
          {
          OID oid;
          data.decode(oid);
@@ -159,17 +159,17 @@ void ASN1_Formatter::decode(std::ostream& output,
 
          output << format(type_tag, class_tag, level, length, out);
          }
-      else if(type_tag == INTEGER || type_tag == ENUMERATED)
+      else if(type_tag == ASN1_Tag::INTEGER || type_tag == ASN1_Tag::ENUMERATED)
          {
          BigInt number;
 
-         if(type_tag == INTEGER)
+         if(type_tag == ASN1_Tag::INTEGER)
             {
             data.decode(number);
             }
-         else if(type_tag == ENUMERATED)
+         else if(type_tag == ASN1_Tag::ENUMERATED)
             {
-            data.decode(number, ENUMERATED, class_tag);
+            data.decode(number, ASN1_Tag::ENUMERATED, class_tag);
             }
 
          std::vector<uint8_t> rep = BigInt::encode(number);
@@ -178,17 +178,17 @@ void ASN1_Formatter::decode(std::ostream& output,
 
          output << format(type_tag, class_tag, level, length, hex_encode(rep));
          }
-      else if(type_tag == BOOLEAN)
+      else if(type_tag == ASN1_Tag::BOOLEAN)
          {
          bool boolean;
          data.decode(boolean);
          output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
          }
-      else if(type_tag == NULL_TAG)
+      else if(type_tag == ASN1_Tag::NULL_TAG)
          {
          output << format(type_tag, class_tag, level, length, "");
          }
-      else if(type_tag == OCTET_STRING || type_tag == BIT_STRING)
+      else if(type_tag == ASN1_Tag::OCTET_STRING || type_tag == ASN1_Tag::BIT_STRING)
          {
          std::vector<uint8_t> decoded_bits;
          data.decode(decoded_bits, type_tag);
@@ -224,7 +224,7 @@ void ASN1_Formatter::decode(std::ostream& output,
          data.decode(str);
          output << format(type_tag, class_tag, level, length, str.value());
          }
-      else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
+      else if(type_tag == ASN1_Tag::UTC_TIME || type_tag == ASN1_Tag::GENERALIZED_TIME)
          {
          ASN1_Time time;
          data.decode(time);
@@ -244,24 +244,24 @@ namespace {
 
 std::string format_type(ASN1_Tag type_tag, ASN1_Tag class_tag)
    {
-   if(class_tag == UNIVERSAL)
+   if(class_tag == ASN1_Tag::UNIVERSAL)
       return asn1_tag_to_string(type_tag);
 
-   if(class_tag == CONSTRUCTED && (type_tag == SEQUENCE || type_tag == SET))
+   if(class_tag == ASN1_Tag::CONSTRUCTED && (type_tag == ASN1_Tag::SEQUENCE || type_tag == ASN1_Tag::SET))
       return asn1_tag_to_string(type_tag);
 
    std::string name;
 
-   if(class_tag & CONSTRUCTED)
+   if(intersects(class_tag, ASN1_Tag::CONSTRUCTED))
       name += "cons ";
 
-   name += "[" + std::to_string(type_tag) + "]";
+   name += "[" + std::to_string(static_cast<uint32_t>(type_tag)) + "]";
 
-   if(class_tag & APPLICATION)
+   if(intersects(class_tag, ASN1_Tag::APPLICATION))
       {
       name += " appl";
       }
-   if(class_tag & CONTEXT_SPECIFIC)
+   if(intersects(class_tag, ASN1_Tag::CONTEXT_SPECIFIC))
       {
       name += " context";
       }
@@ -284,7 +284,7 @@ std::string ASN1_Pretty_Printer::format(ASN1_Tag type_tag,
       should_skip = true;
       }
 
-   if((type_tag == OCTET_STRING || type_tag == BIT_STRING) &&
+   if((type_tag == ASN1_Tag::OCTET_STRING || type_tag == ASN1_Tag::BIT_STRING) &&
       value.length() > m_print_binary_limit)
       {
       should_skip = true;
diff --git a/src/lib/asn1/asn1_str.cpp b/src/lib/asn1/asn1_str.cpp
index ed338832b..877cf05f7 100644
--- a/src/lib/asn1/asn1_str.cpp
+++ b/src/lib/asn1/asn1_str.cpp
@@ -47,10 +47,10 @@ ASN1_Tag choose_encoding(const std::string& str)
       {
       if(!IS_PRINTABLE[static_cast<uint8_t>(str[i])])
          {
-         return UTF8_STRING;
+         return ASN1_Tag::UTF8_STRING;
          }
       }
-   return PRINTABLE_STRING;
+   return ASN1_Tag::PRINTABLE_STRING;
    }
 
 void assert_is_string_type(ASN1_Tag tag)
@@ -58,7 +58,7 @@ void assert_is_string_type(ASN1_Tag tag)
    if(!ASN1_String::is_string_type(tag))
       {
       throw Invalid_Argument("ASN1_String: Unknown string type " +
-                             std::to_string(tag));
+                             std::to_string(static_cast<uint32_t>(tag)));
       }
    }
 
@@ -67,14 +67,14 @@ void assert_is_string_type(ASN1_Tag tag)
 //static
 bool ASN1_String::is_string_type(ASN1_Tag tag)
    {
-   return (tag == NUMERIC_STRING ||
-           tag == PRINTABLE_STRING ||
-           tag == VISIBLE_STRING ||
-           tag == T61_STRING ||
-           tag == IA5_STRING ||
-           tag == UTF8_STRING ||
-           tag == BMP_STRING ||
-           tag == UNIVERSAL_STRING);
+   return (tag == ASN1_Tag::NUMERIC_STRING ||
+           tag == ASN1_Tag::PRINTABLE_STRING ||
+           tag == ASN1_Tag::VISIBLE_STRING ||
+           tag == ASN1_Tag::T61_STRING ||
+           tag == ASN1_Tag::IA5_STRING ||
+           tag == ASN1_Tag::UTF8_STRING ||
+           tag == ASN1_Tag::BMP_STRING ||
+           tag == ASN1_Tag::UNIVERSAL_STRING);
    }
 
 
@@ -83,7 +83,7 @@ bool ASN1_String::is_string_type(ASN1_Tag tag)
 */
 ASN1_String::ASN1_String(const std::string& str, ASN1_Tag t) : m_utf8_str(str), m_tag(t)
    {
-   if(m_tag == DIRECTORY_STRING)
+   if(m_tag == ASN1_Tag::DIRECTORY_STRING)
       {
       m_tag = choose_encoding(m_utf8_str);
       }
@@ -106,12 +106,12 @@ void ASN1_String::encode_into(DER_Encoder& encoder) const
    {
    if(m_data.empty())
       {
-      encoder.add_object(tagging(), UNIVERSAL, m_utf8_str);
+      encoder.add_object(tagging(), ASN1_Tag::UNIVERSAL, m_utf8_str);
       }
    else
       {
       // If this string was decoded, reserialize using original encoding
-      encoder.add_object(tagging(), UNIVERSAL, m_data.data(), m_data.size());
+      encoder.add_object(tagging(), ASN1_Tag::UNIVERSAL, m_data.data(), m_data.size());
       }
    }
 
@@ -127,11 +127,11 @@ void ASN1_String::decode_from(BER_Decoder& source)
    m_tag = obj.type();
    m_data.assign(obj.bits(), obj.bits() + obj.length());
 
-   if(m_tag == BMP_STRING)
+   if(m_tag == ASN1_Tag::BMP_STRING)
       {
       m_utf8_str = ucs2_to_utf8(m_data.data(), m_data.size());
       }
-   else if(m_tag == UNIVERSAL_STRING)
+   else if(m_tag == ASN1_Tag::UNIVERSAL_STRING)
       {
       m_utf8_str = ucs4_to_utf8(m_data.data(), m_data.size());
       }
diff --git a/src/lib/asn1/asn1_time.cpp b/src/lib/asn1/asn1_time.cpp
index 7acaed5d8..927402f74 100644
--- a/src/lib/asn1/asn1_time.cpp
+++ b/src/lib/asn1/asn1_time.cpp
@@ -27,7 +27,7 @@ ASN1_Time::ASN1_Time(const std::chrono::system_clock::time_point& time)
    m_minute = cal.minutes();
    m_second = cal.seconds();
 
-   m_tag = (m_year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
+   m_tag = (m_year >= 2050) ? ASN1_Tag::GENERALIZED_TIME : ASN1_Tag::UTC_TIME;
    }
 
 ASN1_Time::ASN1_Time(const std::string& t_spec, ASN1_Tag tag)
@@ -37,10 +37,10 @@ ASN1_Time::ASN1_Time(const std::string& t_spec, ASN1_Tag tag)
 
 void ASN1_Time::encode_into(DER_Encoder& der) const
    {
-   BOTAN_ARG_CHECK(m_tag == UTC_TIME || m_tag == GENERALIZED_TIME,
+   BOTAN_ARG_CHECK(m_tag == ASN1_Tag::UTC_TIME || m_tag == ASN1_Tag::GENERALIZED_TIME,
                    "ASN1_Time: Bad encoding tag");
 
-   der.add_object(m_tag, UNIVERSAL, to_string());
+   der.add_object(m_tag, ASN1_Tag::UNIVERSAL, to_string());
    }
 
 void ASN1_Time::decode_from(BER_Decoder& source)
@@ -57,7 +57,7 @@ std::string ASN1_Time::to_string() const
 
    uint32_t full_year = m_year;
 
-   if(m_tag == UTC_TIME)
+   if(m_tag == ASN1_Tag::UTC_TIME)
       {
       if(m_year < 1950 || m_year >= 2050)
          throw Encoding_Error("ASN1_Time: The time " + readable_string() +
@@ -66,23 +66,23 @@ std::string ASN1_Time::to_string() const
       full_year = (m_year >= 2000) ? (m_year - 2000) : (m_year - 1900);
       }
 
-   const uint64_t YEAR_FACTOR = 10000000000ULL;
-   const uint64_t MON_FACTOR  = 100000000;
-   const uint64_t DAY_FACTOR  = 1000000;
-   const uint64_t HOUR_FACTOR = 10000;
-   const uint64_t MIN_FACTOR  = 100;
+   const uint64_t year_factor = 10000000000;
+   const uint64_t mon_factor  = 100000000;
+   const uint64_t day_factor  = 1000000;
+   const uint64_t hour_factor = 10000;
+   const uint64_t min_factor  = 100;
 
    const uint64_t int_repr =
-      YEAR_FACTOR * full_year +
-      MON_FACTOR * m_month +
-      DAY_FACTOR * m_day +
-      HOUR_FACTOR * m_hour +
-      MIN_FACTOR * m_minute +
+      year_factor * full_year +
+      mon_factor * m_month +
+      day_factor * m_day +
+      hour_factor * m_hour +
+      min_factor * m_minute +
       m_second;
 
    std::string repr = std::to_string(int_repr) + "Z";
 
-   uint32_t desired_size = (m_tag == UTC_TIME) ? 13 : 15;
+   uint32_t desired_size = (m_tag == ASN1_Tag::UTC_TIME) ? 13 : 15;
 
    while(repr.size() < desired_size)
       repr = "0" + repr;
@@ -140,18 +140,18 @@ int32_t ASN1_Time::cmp(const ASN1_Time& other) const
 
 void ASN1_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
    {
-   if(spec_tag == UTC_OR_GENERALIZED_TIME)
+   if(spec_tag == ASN1_Tag::UTC_OR_GENERALIZED_TIME)
       {
       try
          {
-         set_to(t_spec, GENERALIZED_TIME);
+         set_to(t_spec, ASN1_Tag::GENERALIZED_TIME);
          return;
          }
       catch(Invalid_Argument&) {} // Not a generalized time. Continue
 
       try
          {
-         set_to(t_spec, UTC_TIME);
+         set_to(t_spec, ASN1_Tag::UTC_TIME);
          return;
          }
       catch(Invalid_Argument&) {} // Not a UTC time. Continue
@@ -159,22 +159,22 @@ void ASN1_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
       throw Invalid_Argument("Time string could not be parsed as GeneralizedTime or UTCTime.");
       }
 
-   BOTAN_ASSERT(spec_tag == UTC_TIME || spec_tag == GENERALIZED_TIME, "Invalid tag.");
+   BOTAN_ASSERT(spec_tag == ASN1_Tag::UTC_TIME || spec_tag == ASN1_Tag::GENERALIZED_TIME, "Invalid tag.");
 
    BOTAN_ARG_CHECK(t_spec.size() > 0, "Time string must not be empty.");
 
    BOTAN_ARG_CHECK(t_spec.back() == 'Z', "Botan does not support times with timezones other than Z");
 
-   if(spec_tag == GENERALIZED_TIME)
+   if(spec_tag == ASN1_Tag::GENERALIZED_TIME)
       {
       BOTAN_ARG_CHECK(t_spec.size() == 15, "Invalid GeneralizedTime string");
       }
-   else if(spec_tag == UTC_TIME)
+   else if(spec_tag == ASN1_Tag::UTC_TIME)
       {
       BOTAN_ARG_CHECK(t_spec.size() == 13, "Invalid UTCTime string");
       }
 
-   const size_t YEAR_SIZE = (spec_tag == UTC_TIME) ? 2 : 4;
+   const size_t YEAR_SIZE = (spec_tag == ASN1_Tag::UTC_TIME) ? 2 : 4;
 
    std::vector<std::string> params;
    std::string current;
@@ -202,7 +202,7 @@ void ASN1_Time::set_to(const std::string& t_spec, ASN1_Tag spec_tag)
    m_second = (params.size() == 6) ? to_u32bit(params[5]) : 0;
    m_tag    = spec_tag;
 
-   if(spec_tag == UTC_TIME)
+   if(spec_tag == ASN1_Tag::UTC_TIME)
       {
       if(m_year >= 50) m_year += 1900;
       else             m_year += 2000;
@@ -240,7 +240,7 @@ bool ASN1_Time::passes_sanity_check() const
    if(m_hour >= 24 || m_minute >= 60 || m_second > 60)
       return false;
 
-   if (m_tag == UTC_TIME)
+   if (m_tag == ASN1_Tag::UTC_TIME)
       {
       /*
       UTCTime limits the value of components such that leap seconds
diff --git a/src/lib/asn1/ber_dec.cpp b/src/lib/asn1/ber_dec.cpp
index 8ccd80c2a..968a8ad4d 100644
--- a/src/lib/asn1/ber_dec.cpp
+++ b/src/lib/asn1/ber_dec.cpp
@@ -29,7 +29,7 @@ size_t decode_tag(DataSource* ber, ASN1_Tag& type_tag, ASN1_Tag& class_tag)
    uint8_t b;
    if(!ber->read_byte(b))
       {
-      class_tag = type_tag = NO_OBJECT;
+      class_tag = type_tag = ASN1_Tag::NO_OBJECT;
       return 0;
       }
 
@@ -128,7 +128,7 @@ size_t find_eoc(DataSource* ber, size_t allow_indef)
       {
       ASN1_Tag type_tag, class_tag;
       size_t tag_size = decode_tag(&source, type_tag, class_tag);
-      if(type_tag == NO_OBJECT)
+      if(type_tag == ASN1_Tag::NO_OBJECT)
          break;
 
       size_t length_size = 0;
@@ -139,7 +139,7 @@ size_t find_eoc(DataSource* ber, size_t allow_indef)
       length = BOTAN_CHECKED_ADD(length, tag_size);
       length = BOTAN_CHECKED_ADD(length, length_size);
 
-      if(type_tag == EOC && class_tag == UNIVERSAL)
+      if(type_tag == ASN1_Tag::EOC && class_tag == ASN1_Tag::UNIVERSAL)
          break;
       }
    return length;
@@ -261,7 +261,7 @@ BER_Object BER_Decoder::get_next_object()
       if(m_source->read(out, length) != length)
          throw BER_Decoding_Error("Value truncated");
 
-      if(next.tagging() == EOC)
+      if(next.tagging() == ASN1_Tag::EOC)
          continue;
       else
          break;
@@ -290,7 +290,7 @@ void BER_Decoder::push_back(BER_Object&& obj)
 BER_Decoder BER_Decoder::start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag)
    {
    BER_Object obj = get_next_object();
-   obj.assert_is_a(type_tag, ASN1_Tag(class_tag | CONSTRUCTED));
+   obj.assert_is_a(type_tag, class_tag | ASN1_Tag::CONSTRUCTED);
    return BER_Decoder(std::move(obj), this);
    }
 
@@ -376,7 +376,7 @@ BER_Decoder& BER_Decoder::decode(ASN1_Object& obj,
 BER_Decoder& BER_Decoder::decode_null()
    {
    BER_Object obj = get_next_object();
-   obj.assert_is_a(NULL_TAG, UNIVERSAL);
+   obj.assert_is_a(ASN1_Tag::NULL_TAG, ASN1_Tag::UNIVERSAL);
    if(obj.length() > 0)
       throw BER_Decoding_Error("NULL object had nonzero size");
    return (*this);
@@ -385,7 +385,7 @@ BER_Decoder& BER_Decoder::decode_null()
 BER_Decoder& BER_Decoder::decode_octet_string_bigint(BigInt& out)
    {
    secure_vector<uint8_t> out_vec;
-   decode(out_vec, OCTET_STRING);
+   decode(out_vec, ASN1_Tag::OCTET_STRING);
    out = BigInt::decode(out_vec.data(), out_vec.size());
    return (*this);
    }
@@ -501,7 +501,7 @@ void asn1_decode_binary_string(std::vector<uint8_t, Alloc>& buffer,
    {
    obj.assert_is_a(type_tag, class_tag);
 
-   if(real_type == OCTET_STRING)
+   if(real_type == ASN1_Tag::OCTET_STRING)
       {
       buffer.assign(obj.bits(), obj.bits() + obj.length());
       }
@@ -528,7 +528,7 @@ BER_Decoder& BER_Decoder::decode(secure_vector<uint8_t>& buffer,
                                  ASN1_Tag real_type,
                                  ASN1_Tag type_tag, ASN1_Tag class_tag)
    {
-   if(real_type != OCTET_STRING && real_type != BIT_STRING)
+   if(real_type != ASN1_Tag::OCTET_STRING && real_type != ASN1_Tag::BIT_STRING)
       throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
 
    asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
@@ -539,7 +539,7 @@ BER_Decoder& BER_Decoder::decode(std::vector<uint8_t>& buffer,
                                  ASN1_Tag real_type,
                                  ASN1_Tag type_tag, ASN1_Tag class_tag)
    {
-   if(real_type != OCTET_STRING && real_type != BIT_STRING)
+   if(real_type != ASN1_Tag::OCTET_STRING && real_type != ASN1_Tag::BIT_STRING)
       throw BER_Bad_Tag("Bad tag for {BIT,OCTET} STRING", real_type);
 
    asn1_decode_binary_string(buffer, get_next_object(), real_type, type_tag, class_tag);
diff --git a/src/lib/asn1/ber_dec.h b/src/lib/asn1/ber_dec.h
index 1fb8c4a9d..4d96a8d3a 100644
--- a/src/lib/asn1/ber_dec.h
+++ b/src/lib/asn1/ber_dec.h
@@ -109,7 +109,12 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       /**
       * Start decoding a constructed data (sequence or set)
       */
-      BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL);
+      BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = ASN1_Tag::UNIVERSAL);
+
+      BER_Decoder start_sequence()
+         {
+         return start_cons(ASN1_Tag::SEQUENCE);
+         }
 
       /**
       * Finish decoding a constructed data, throws if any data remains.
@@ -130,7 +135,7 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       template <typename T>
          BER_Decoder& get_next_value(T &out,
                                      ASN1_Tag type_tag,
-                                     ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+                                     ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC)
          {
          static_assert(std::is_standard_layout<T>::value && std::is_trivial<T>::value, "Type must be POD");
 
@@ -169,7 +174,7 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       */
       BER_Decoder& decode(bool& out)
          {
-         return decode(out, BOOLEAN, UNIVERSAL);
+         return decode(out, ASN1_Tag::BOOLEAN, ASN1_Tag::UNIVERSAL);
          }
 
       /*
@@ -177,7 +182,7 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       */
       BER_Decoder& decode(size_t& out)
          {
-         return decode(out, INTEGER, UNIVERSAL);
+         return decode(out, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL);
          }
 
       /*
@@ -185,13 +190,13 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       */
       BER_Decoder& decode(BigInt& out)
          {
-         return decode(out, INTEGER, UNIVERSAL);
+         return decode(out, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL);
          }
 
       std::vector<uint8_t> get_next_octet_string()
          {
          std::vector<uint8_t> out_vec;
-         decode(out_vec, OCTET_STRING);
+         decode(out_vec, ASN1_Tag::OCTET_STRING);
          return out_vec;
          }
 
@@ -201,34 +206,34 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       template<typename Alloc>
       BER_Decoder& decode(std::vector<uint8_t, Alloc>& out, ASN1_Tag real_type)
          {
-         return decode(out, real_type, real_type, UNIVERSAL);
+         return decode(out, real_type, real_type, ASN1_Tag::UNIVERSAL);
          }
 
       BER_Decoder& decode(bool& v,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       BER_Decoder& decode(size_t& v,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       BER_Decoder& decode(BigInt& v,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       BER_Decoder& decode(std::vector<uint8_t>& v,
                           ASN1_Tag real_type,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       BER_Decoder& decode(secure_vector<uint8_t>& v,
                           ASN1_Tag real_type,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       BER_Decoder& decode(class ASN1_Object& obj,
-                          ASN1_Tag type_tag = NO_OBJECT,
-                          ASN1_Tag class_tag = NO_OBJECT);
+                          ASN1_Tag type_tag = ASN1_Tag::NO_OBJECT,
+                          ASN1_Tag class_tag = ASN1_Tag::NO_OBJECT);
 
       /**
       * Decode an integer value which is typed as an octet string
@@ -241,13 +246,13 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
 
       template<typename T> BER_Decoder& decode_integer_type(T& out)
          {
-         return decode_integer_type<T>(out, INTEGER, UNIVERSAL);
+         return decode_integer_type<T>(out, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL);
          }
 
       template<typename T>
          BER_Decoder& decode_integer_type(T& out,
                                           ASN1_Tag type_tag,
-                                          ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+                                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC)
          {
          out = static_cast<T>(decode_constrained_integer(type_tag, class_tag, sizeof(out)));
          return (*this);
@@ -270,8 +275,8 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
 
       template<typename T>
          BER_Decoder& decode_list(std::vector<T>& out,
-                                  ASN1_Tag type_tag = SEQUENCE,
-                                  ASN1_Tag class_tag = UNIVERSAL);
+                                  ASN1_Tag type_tag = ASN1_Tag::SEQUENCE,
+                                  ASN1_Tag class_tag = ASN1_Tag::UNIVERSAL);
 
       template<typename T>
          BER_Decoder& decode_and_check(const T& expected,
@@ -292,16 +297,16 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
       template<typename Alloc>
       BER_Decoder& decode_optional_string(std::vector<uint8_t, Alloc>& out,
                                           ASN1_Tag real_type,
-                                          uint16_t type_no,
-                                          ASN1_Tag class_tag = CONTEXT_SPECIFIC)
+                                          uint32_t expected_tag,
+                                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC)
          {
          BER_Object obj = get_next_object();
 
-         ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
+         ASN1_Tag type_tag = static_cast<ASN1_Tag>(expected_tag);
 
          if(obj.is_a(type_tag, class_tag))
             {
-            if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+            if(class_tag == ASN1_Tag::PRIVATE)
                {
                BER_Decoder(std::move(obj)).decode(out, real_type).verify_end();
                }
@@ -320,6 +325,17 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final
          return (*this);
          }
 
+      template<typename Alloc>
+      BER_Decoder& decode_optional_string(std::vector<uint8_t, Alloc>& out,
+                                          ASN1_Tag real_type,
+                                          ASN1_Tag expected_tag,
+                                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC)
+         {
+         return decode_optional_string(out, real_type,
+                                       static_cast<uint32_t>(expected_tag),
+                                       class_tag);
+         }
+
    private:
       BER_Decoder(BER_Object&& obj, BER_Decoder* parent);
 
@@ -343,7 +359,7 @@ BER_Decoder& BER_Decoder::decode_optional(T& out,
 
    if(obj.is_a(type_tag, class_tag))
       {
-      if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC))
+      if(class_tag == ASN1_Tag::PRIVATE)
          {
          BER_Decoder(std::move(obj)).decode(out).verify_end();
          }
diff --git a/src/lib/asn1/der_enc.cpp b/src/lib/asn1/der_enc.cpp
index 88534efef..bb65e38ee 100644
--- a/src/lib/asn1/der_enc.cpp
+++ b/src/lib/asn1/der_enc.cpp
@@ -20,8 +20,11 @@ namespace {
 * DER encode an ASN.1 type tag
 */
 void encode_tag(std::vector<uint8_t>& encoded_tag,
-                ASN1_Tag type_tag, ASN1_Tag class_tag)
+                ASN1_Tag type_tag_e, ASN1_Tag class_tag_e)
    {
+   const uint32_t type_tag = static_cast<uint32_t>(type_tag_e);
+   const uint32_t class_tag = static_cast<uint32_t>(class_tag_e);
+
    if((class_tag | 0xE0) != 0xE0)
       throw Encoding_Error("DER_Encoder: Invalid class tag " +
                            std::to_string(class_tag));
@@ -87,9 +90,9 @@ DER_Encoder::DER_Encoder(std::vector<uint8_t>& vec)
 */
 void DER_Encoder::DER_Sequence::push_contents(DER_Encoder& der)
    {
-   const ASN1_Tag real_class_tag = ASN1_Tag(m_class_tag | CONSTRUCTED);
+   const ASN1_Tag real_class_tag = ASN1_Tag(m_class_tag) | ASN1_Tag::CONSTRUCTED;
 
-   if(m_type_tag == SET)
+   if(m_type_tag == ASN1_Tag::SET)
       {
       std::sort(m_set_contents.begin(), m_set_contents.end());
       for(size_t i = 0; i != m_set_contents.size(); ++i)
@@ -106,7 +109,7 @@ void DER_Encoder::DER_Sequence::push_contents(DER_Encoder& der)
 */
 void DER_Encoder::DER_Sequence::add_bytes(const uint8_t data[], size_t length)
    {
-   if(m_type_tag == SET)
+   if(m_type_tag == ASN1_Tag::SET)
       m_set_contents.push_back(secure_vector<uint8_t>(data, data + length));
    else
       m_contents += std::make_pair(data, length);
@@ -115,7 +118,7 @@ void DER_Encoder::DER_Sequence::add_bytes(const uint8_t data[], size_t length)
 void DER_Encoder::DER_Sequence::add_bytes(const uint8_t hdr[], size_t hdr_len,
                                           const uint8_t val[], size_t val_len)
    {
-   if(m_type_tag == SET)
+   if(m_type_tag == ASN1_Tag::SET)
       {
       secure_vector<uint8_t> m;
       m.reserve(hdr_len + val_len);
@@ -208,10 +211,10 @@ DER_Encoder& DER_Encoder::start_explicit(uint16_t type_no)
    ASN1_Tag type_tag = static_cast<ASN1_Tag>(type_no);
 
    // This would confuse DER_Sequence
-   if(type_tag == SET)
+   if(type_tag == ASN1_Tag::SET)
       throw Internal_Error("DER_Encoder.start_explicit(SET) not supported");
 
-   return start_cons(type_tag, CONTEXT_SPECIFIC);
+   return start_cons(type_tag, ASN1_Tag::CONTEXT_SPECIFIC);
    }
 
 /*
@@ -276,7 +279,7 @@ DER_Encoder& DER_Encoder::add_object(ASN1_Tag type_tag, ASN1_Tag class_tag,
 */
 DER_Encoder& DER_Encoder::encode_null()
    {
-   return add_object(NULL_TAG, UNIVERSAL, nullptr, 0);
+   return add_object(ASN1_Tag::NULL_TAG, ASN1_Tag::UNIVERSAL, nullptr, 0);
    }
 
 /*
@@ -284,7 +287,7 @@ DER_Encoder& DER_Encoder::encode_null()
 */
 DER_Encoder& DER_Encoder::encode(bool is_true)
    {
-   return encode(is_true, BOOLEAN, UNIVERSAL);
+   return encode(is_true, ASN1_Tag::BOOLEAN, ASN1_Tag::UNIVERSAL);
    }
 
 /*
@@ -292,7 +295,7 @@ DER_Encoder& DER_Encoder::encode(bool is_true)
 */
 DER_Encoder& DER_Encoder::encode(size_t n)
    {
-   return encode(BigInt(n), INTEGER, UNIVERSAL);
+   return encode(BigInt(n), ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL);
    }
 
 /*
@@ -300,7 +303,7 @@ DER_Encoder& DER_Encoder::encode(size_t n)
 */
 DER_Encoder& DER_Encoder::encode(const BigInt& n)
    {
-   return encode(n, INTEGER, UNIVERSAL);
+   return encode(n, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL);
    }
 
 /*
@@ -309,7 +312,7 @@ DER_Encoder& DER_Encoder::encode(const BigInt& n)
 DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length,
                                  ASN1_Tag real_type)
    {
-   return encode(bytes, length, real_type, real_type, UNIVERSAL);
+   return encode(bytes, length, real_type, real_type, ASN1_Tag::UNIVERSAL);
    }
 
 /*
@@ -362,10 +365,10 @@ DER_Encoder& DER_Encoder::encode(const uint8_t bytes[], size_t length,
                                  ASN1_Tag real_type,
                                  ASN1_Tag type_tag, ASN1_Tag class_tag)
    {
-   if(real_type != OCTET_STRING && real_type != BIT_STRING)
+   if(real_type != ASN1_Tag::OCTET_STRING && real_type != ASN1_Tag::BIT_STRING)
       throw Invalid_Argument("DER_Encoder: Invalid tag for byte/bit string");
 
-   if(real_type == BIT_STRING)
+   if(real_type == ASN1_Tag::BIT_STRING)
       {
       secure_vector<uint8_t> encoded;
       encoded.push_back(0);
diff --git a/src/lib/asn1/der_enc.h b/src/lib/asn1/der_enc.h
index 8e991e09d..ff23a5096 100644
--- a/src/lib/asn1/der_enc.h
+++ b/src/lib/asn1/der_enc.h
@@ -62,7 +62,13 @@ class BOTAN_PUBLIC_API(2,0) DER_Encoder final
       std::vector<uint8_t> get_contents_unlocked();
 
       DER_Encoder& start_cons(ASN1_Tag type_tag,
-                              ASN1_Tag class_tag = UNIVERSAL);
+                              ASN1_Tag class_tag = ASN1_Tag::UNIVERSAL);
+
+      DER_Encoder& start_sequence()
+         {
+         return start_cons(ASN1_Tag::SEQUENCE);
+         }
+
       DER_Encoder& end_cons();
 
       DER_Encoder& start_explicit(uint16_t type_tag);
@@ -93,20 +99,20 @@ class BOTAN_PUBLIC_API(2,0) DER_Encoder final
 
       DER_Encoder& encode(bool b,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       DER_Encoder& encode(size_t s,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       DER_Encoder& encode(const BigInt& n,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       DER_Encoder& encode(const uint8_t v[], size_t len,
                           ASN1_Tag real_type,
                           ASN1_Tag type_tag,
-                          ASN1_Tag class_tag = CONTEXT_SPECIFIC);
+                          ASN1_Tag class_tag = ASN1_Tag::CONTEXT_SPECIFIC);
 
       template<typename Alloc>
       DER_Encoder& encode(const std::vector<uint8_t, Alloc>& bytes,
diff --git a/src/lib/kdf/prf_x942/prf_x942.cpp b/src/lib/kdf/prf_x942/prf_x942.cpp
index 01c617b07..93dc098cd 100644
--- a/src/lib/kdf/prf_x942/prf_x942.cpp
+++ b/src/lib/kdf/prf_x942/prf_x942.cpp
@@ -24,7 +24,7 @@ std::vector<uint8_t> encode_x942_int(uint32_t n)
    store_be(n, n_buf);
 
    std::vector<uint8_t> output;
-   DER_Encoder(output).encode(n_buf, 4, OCTET_STRING);
+   DER_Encoder(output).encode(n_buf, 4, ASN1_Tag::OCTET_STRING);
    return output;
    }
 
@@ -59,9 +59,9 @@ void X942_PRF::kdf(uint8_t key[], size_t key_len,
       hash->update(secret, secret_len);
 
       hash->update(
-         DER_Encoder().start_cons(SEQUENCE)
+         DER_Encoder().start_sequence()
 
-            .start_cons(SEQUENCE)
+            .start_sequence()
                .encode(m_key_wrap_oid)
                .raw_bytes(encode_x942_int(counter))
             .end_cons()
@@ -69,7 +69,7 @@ void X942_PRF::kdf(uint8_t key[], size_t key_len,
             .encode_if(salt_len != 0,
                DER_Encoder()
                   .start_explicit(0)
-                     .encode(in, OCTET_STRING)
+                    .encode(in, ASN1_Tag::OCTET_STRING)
                   .end_explicit()
                )
 
diff --git a/src/lib/pk_pad/emsa_pssr/pssr.cpp b/src/lib/pk_pad/emsa_pssr/pssr.cpp
index b6e7741ea..a4d28bafe 100644
--- a/src/lib/pk_pad/emsa_pssr/pssr.cpp
+++ b/src/lib/pk_pad/emsa_pssr/pssr.cpp
@@ -207,11 +207,11 @@ AlgorithmIdentifier PSSR::config_for_x509(const Private_Key& key,
 
    std::vector<uint8_t> parameters;
    DER_Encoder(parameters)
-      .start_cons(SEQUENCE)
-      .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).encode(hash_id).end_cons()
-      .start_cons(ASN1_Tag(1), CONTEXT_SPECIFIC).encode(mgf_id).end_cons()
-      .start_cons(ASN1_Tag(2), CONTEXT_SPECIFIC).encode(m_salt_size).end_cons()
-      .start_cons(ASN1_Tag(3), CONTEXT_SPECIFIC).encode(size_t(1)).end_cons() // trailer field
+      .start_sequence()
+      .start_cons(ASN1_Tag(0), ASN1_Tag::CONTEXT_SPECIFIC).encode(hash_id).end_cons()
+      .start_cons(ASN1_Tag(1), ASN1_Tag::CONTEXT_SPECIFIC).encode(mgf_id).end_cons()
+      .start_cons(ASN1_Tag(2), ASN1_Tag::CONTEXT_SPECIFIC).encode(m_salt_size).end_cons()
+      .start_cons(ASN1_Tag(3), ASN1_Tag::CONTEXT_SPECIFIC).encode(size_t(1)).end_cons() // trailer field
       .end_cons();
 
    // hardcoded as RSA is the only valid algorithm for EMSA4 at the moment
diff --git a/src/lib/prov/openssl/openssl_ec.cpp b/src/lib/prov/openssl/openssl_ec.cpp
index bd64aef34..6030256be 100644
--- a/src/lib/prov/openssl/openssl_ec.cpp
+++ b/src/lib/prov/openssl/openssl_ec.cpp
@@ -48,14 +48,14 @@ secure_vector<uint8_t> PKCS8_for_openssl(const EC_PrivateKey& ec)
    const BigInt& priv_key = ec.private_value();
 
    return DER_Encoder()
-     .start_cons(SEQUENCE)
+     .start_sequence()
         .encode(static_cast<size_t>(1))
-        .encode(BigInt::encode_1363(priv_key, priv_key.bytes()), OCTET_STRING)
-      .start_cons(ASN1_Tag(0), PRIVATE)
+        .encode(BigInt::encode_1363(priv_key, priv_key.bytes()), ASN1_Tag::OCTET_STRING)
+      .start_cons(ASN1_Tag(0), ASN1_Tag::PRIVATE)
       .raw_bytes(ec.domain().DER_encode(EC_Group_Encoding::NamedCurve))
       .end_cons()
-      .start_cons(ASN1_Tag(1), PRIVATE)
-      .encode(pub_key.encode(PointGFp::UNCOMPRESSED), BIT_STRING)
+      .start_cons(ASN1_Tag(1), ASN1_Tag::PRIVATE)
+      .encode(pub_key.encode(PointGFp::UNCOMPRESSED), ASN1_Tag::BIT_STRING)
       .end_cons()
       .end_cons()
       .get_contents();
diff --git a/src/lib/prov/pkcs11/p11_ecc_key.cpp b/src/lib/prov/pkcs11/p11_ecc_key.cpp
index 4a2736fc6..b1c8da6dc 100644
--- a/src/lib/prov/pkcs11/p11_ecc_key.cpp
+++ b/src/lib/prov/pkcs11/p11_ecc_key.cpp
@@ -21,7 +21,7 @@ namespace {
 PointGFp decode_public_point(const secure_vector<uint8_t>& ec_point_data, const EC_Group& group)
    {
    secure_vector<uint8_t> ec_point;
-   BER_Decoder(ec_point_data).decode(ec_point, OCTET_STRING);
+   BER_Decoder(ec_point_data).decode(ec_point, ASN1_Tag::OCTET_STRING);
    return group.OS2ECP(ec_point);
    }
 }
@@ -55,7 +55,7 @@ PKCS11_EC_PublicKey::PKCS11_EC_PublicKey(Session& session, const EC_PublicKeyImp
    m_domain_params = EC_Group(props.ec_params());
 
    secure_vector<uint8_t> ec_point;
-   BER_Decoder(props.ec_point()).decode(ec_point, OCTET_STRING);
+   BER_Decoder(props.ec_point()).decode(ec_point, ASN1_Tag::OCTET_STRING);
    m_public_key = m_domain_params.OS2ECP(ec_point);
    m_domain_encoding = EC_Group_Encoding::Explicit;
    }
diff --git a/src/lib/prov/pkcs11/p11_ecdh.cpp b/src/lib/prov/pkcs11/p11_ecdh.cpp
index 82f09e4cd..5e4a15a0d 100644
--- a/src/lib/prov/pkcs11/p11_ecdh.cpp
+++ b/src/lib/prov/pkcs11/p11_ecdh.cpp
@@ -60,7 +60,7 @@ class PKCS11_ECDH_KA_Operation final : public PK_Ops::Key_Agreement
          std::vector<uint8_t> der_encoded_other_key;
          if(m_key.point_encoding() == PublicPointEncoding::Der)
             {
-            DER_Encoder(der_encoded_other_key).encode(other_key, other_key_len, OCTET_STRING);
+            DER_Encoder(der_encoded_other_key).encode(other_key, other_key_len, ASN1_Tag::OCTET_STRING);
             m_mechanism.set_ecdh_other_key(der_encoded_other_key.data(), der_encoded_other_key.size());
             }
          else
diff --git a/src/lib/prov/tpm/tpm.cpp b/src/lib/prov/tpm/tpm.cpp
index d8c6ebd15..d131c1a92 100644
--- a/src/lib/prov/tpm/tpm.cpp
+++ b/src/lib/prov/tpm/tpm.cpp
@@ -354,7 +354,7 @@ std::vector<uint8_t> TPM_PrivateKey::public_key_bits() const
    {
    std::vector<uint8_t> bits;
    DER_Encoder(bits)
-      .start_cons(SEQUENCE)
+      .start_sequence()
         .encode(get_n())
         .encode(get_e())
       .end_cons();
diff --git a/src/lib/pubkey/curve25519/curve25519.cpp b/src/lib/pubkey/curve25519/curve25519.cpp
index a06e700c4..7c562cb6a 100644
--- a/src/lib/pubkey/curve25519/curve25519.cpp
+++ b/src/lib/pubkey/curve25519/curve25519.cpp
@@ -80,7 +80,7 @@ Curve25519_PrivateKey::Curve25519_PrivateKey(RandomNumberGenerator& rng)
 Curve25519_PrivateKey::Curve25519_PrivateKey(const AlgorithmIdentifier&,
                                              const secure_vector<uint8_t>& key_bits)
    {
-   BER_Decoder(key_bits).decode(m_private, OCTET_STRING).discard_remaining();
+   BER_Decoder(key_bits).decode(m_private, ASN1_Tag::OCTET_STRING).discard_remaining();
 
    size_check(m_private.size(), "private key");
    m_public.resize(32);
@@ -94,7 +94,7 @@ std::unique_ptr<Public_Key> Curve25519_PrivateKey::public_key() const
 
 secure_vector<uint8_t> Curve25519_PrivateKey::private_key_bits() const
    {
-   return DER_Encoder().encode(m_private, OCTET_STRING).get_contents();
+   return DER_Encoder().encode(m_private, ASN1_Tag::OCTET_STRING).get_contents();
    }
 
 bool Curve25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const
diff --git a/src/lib/pubkey/dl_group/dl_group.cpp b/src/lib/pubkey/dl_group/dl_group.cpp
index 05fe9dcf3..26ea39f17 100644
--- a/src/lib/pubkey/dl_group/dl_group.cpp
+++ b/src/lib/pubkey/dl_group/dl_group.cpp
@@ -129,7 +129,7 @@ std::shared_ptr<DL_Group_Data> DL_Group::BER_decode_DL_group(const uint8_t data[
    BigInt p, q, g;
 
    BER_Decoder decoder(data, data_len);
-   BER_Decoder ber = decoder.start_cons(SEQUENCE);
+   BER_Decoder ber = decoder.start_sequence();
 
    if(format == DL_Group_Format::ANSI_X9_57)
       {
@@ -599,7 +599,7 @@ std::vector<uint8_t> DL_Group::DER_encode(DL_Group_Format format) const
 
    if(format == DL_Group_Format::ANSI_X9_57)
       {
-      der.start_cons(SEQUENCE)
+      der.start_sequence()
             .encode(get_p())
             .encode(get_q())
             .encode(get_g())
@@ -607,7 +607,7 @@ std::vector<uint8_t> DL_Group::DER_encode(DL_Group_Format format) const
       }
    else if(format == DL_Group_Format::ANSI_X9_42)
       {
-      der.start_cons(SEQUENCE)
+      der.start_sequence()
             .encode(get_p())
             .encode(get_g())
             .encode(get_q())
@@ -615,7 +615,7 @@ std::vector<uint8_t> DL_Group::DER_encode(DL_Group_Format format) const
       }
    else if(format == DL_Group_Format::PKCS_3)
       {
-      der.start_cons(SEQUENCE)
+      der.start_sequence()
             .encode(get_p())
             .encode(get_g())
          .end_cons();
diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp
index 9eca26c45..4509bc740 100644
--- a/src/lib/pubkey/ec_group/ec_group.cpp
+++ b/src/lib/pubkey/ec_group/ec_group.cpp
@@ -299,36 +299,36 @@ std::shared_ptr<EC_Group_Data> EC_Group::BER_decode_EC_group(const uint8_t bits[
    BER_Decoder ber(bits, len);
    BER_Object obj = ber.get_next_object();
 
-   if(obj.type() == NULL_TAG)
+   if(obj.type() == ASN1_Tag::NULL_TAG)
       {
       throw Decoding_Error("Cannot handle ImplicitCA ECC parameters");
       }
-   else if(obj.type() == OBJECT_ID)
+   else if(obj.type() == ASN1_Tag::OBJECT_ID)
       {
       OID dom_par_oid;
       BER_Decoder(bits, len).decode(dom_par_oid);
       return ec_group_data().lookup(dom_par_oid);
       }
-   else if(obj.type() == SEQUENCE)
+   else if(obj.type() == ASN1_Tag::SEQUENCE)
       {
       BigInt p, a, b, order, cofactor;
       std::vector<uint8_t> base_pt;
       std::vector<uint8_t> seed;
 
       BER_Decoder(bits, len)
-         .start_cons(SEQUENCE)
+         .start_sequence()
            .decode_and_check<size_t>(1, "Unknown ECC param version code")
-           .start_cons(SEQUENCE)
+           .start_sequence()
             .decode_and_check(OID("1.2.840.10045.1.1"),
                               "Only prime ECC fields supported")
              .decode(p)
            .end_cons()
-           .start_cons(SEQUENCE)
+           .start_sequence()
              .decode_octet_string_bigint(a)
              .decode_octet_string_bigint(b)
-             .decode_optional_string(seed, BIT_STRING, BIT_STRING)
+             .decode_optional_string(seed, ASN1_Tag::BIT_STRING, ASN1_Tag::BIT_STRING)
            .end_cons()
-           .decode(base_pt, OCTET_STRING)
+           .decode(base_pt, ASN1_Tag::OCTET_STRING)
            .decode(order)
            .decode(cofactor)
          .end_cons()
@@ -619,19 +619,19 @@ EC_Group::DER_encode(EC_Group_Encoding form) const
 
       const size_t p_bytes = get_p_bytes();
 
-      der.start_cons(SEQUENCE)
+      der.start_sequence()
             .encode(ecpVers1)
-            .start_cons(SEQUENCE)
+            .start_sequence()
                .encode(curve_type)
                .encode(get_p())
             .end_cons()
-            .start_cons(SEQUENCE)
+            .start_sequence()
                .encode(BigInt::encode_1363(get_a(), p_bytes),
-                       OCTET_STRING)
+                       ASN1_Tag::OCTET_STRING)
                .encode(BigInt::encode_1363(get_b(), p_bytes),
-                       OCTET_STRING)
+                       ASN1_Tag::OCTET_STRING)
             .end_cons()
-              .encode(get_base_point().encode(PointGFp::UNCOMPRESSED), OCTET_STRING)
+              .encode(get_base_point().encode(PointGFp::UNCOMPRESSED), ASN1_Tag::OCTET_STRING)
             .encode(get_order())
             .encode(get_cofactor())
          .end_cons();
diff --git a/src/lib/pubkey/ecc_key/ecc_key.cpp b/src/lib/pubkey/ecc_key/ecc_key.cpp
index 90ffb260b..310c568d3 100644
--- a/src/lib/pubkey/ecc_key/ecc_key.cpp
+++ b/src/lib/pubkey/ecc_key/ecc_key.cpp
@@ -143,11 +143,11 @@ EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng,
 secure_vector<uint8_t> EC_PrivateKey::private_key_bits() const
    {
    return DER_Encoder()
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode(static_cast<size_t>(1))
-         .encode(BigInt::encode_1363(m_private_key, m_private_key.bytes()), OCTET_STRING)
-         .start_cons(ASN1_Tag(1), PRIVATE)
-            .encode(m_public_key.encode(PointGFp::Compression_Type::UNCOMPRESSED), BIT_STRING)
+         .encode(BigInt::encode_1363(m_private_key, m_private_key.bytes()), ASN1_Tag::OCTET_STRING)
+         .start_cons(ASN1_Tag(1), ASN1_Tag::PRIVATE)
+            .encode(m_public_key.encode(PointGFp::Compression_Type::UNCOMPRESSED), ASN1_Tag::BIT_STRING)
          .end_cons()
       .end_cons()
       .get_contents();
@@ -164,11 +164,11 @@ EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id,
    secure_vector<uint8_t> public_key_bits;
 
    BER_Decoder(key_bits)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .decode_and_check<size_t>(1, "Unknown version code for ECC key")
          .decode_octet_string_bigint(m_private_key)
-         .decode_optional(key_parameters, ASN1_Tag(0), PRIVATE)
-         .decode_optional_string(public_key_bits, BIT_STRING, 1, PRIVATE)
+         .decode_optional(key_parameters, ASN1_Tag(0), ASN1_Tag::PRIVATE)
+         .decode_optional_string(public_key_bits, ASN1_Tag::BIT_STRING, 1, ASN1_Tag::PRIVATE)
       .end_cons();
 
    if(public_key_bits.empty())
diff --git a/src/lib/pubkey/ed25519/ed25519_key.cpp b/src/lib/pubkey/ed25519/ed25519_key.cpp
index ad8646111..464d6fdba 100644
--- a/src/lib/pubkey/ed25519/ed25519_key.cpp
+++ b/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -78,7 +78,7 @@ Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier&,
                                        const secure_vector<uint8_t>& key_bits)
    {
    secure_vector<uint8_t> bits;
-   BER_Decoder(key_bits).decode(bits, OCTET_STRING).discard_remaining();
+   BER_Decoder(key_bits).decode(bits, ASN1_Tag::OCTET_STRING).discard_remaining();
 
    if(bits.size() != 32)
       throw Decoding_Error("Invalid size for Ed25519 private key");
@@ -95,7 +95,7 @@ std::unique_ptr<Public_Key> Ed25519_PrivateKey::public_key() const
 secure_vector<uint8_t> Ed25519_PrivateKey::private_key_bits() const
    {
    secure_vector<uint8_t> bits(&m_private[0], &m_private[32]);
-   return DER_Encoder().encode(bits, OCTET_STRING).get_contents();
+   return DER_Encoder().encode(bits, ASN1_Tag::OCTET_STRING).get_contents();
    }
 
 bool Ed25519_PrivateKey::check_key(RandomNumberGenerator&, bool) const
diff --git a/src/lib/pubkey/gost_3410/gost_3410.cpp b/src/lib/pubkey/gost_3410/gost_3410.cpp
index f378a7998..6b813963c 100644
--- a/src/lib/pubkey/gost_3410/gost_3410.cpp
+++ b/src/lib/pubkey/gost_3410/gost_3410.cpp
@@ -36,7 +36,7 @@ std::vector<uint8_t> GOST_3410_PublicKey::public_key_bits() const
       }
 
    std::vector<uint8_t> output;
-   DER_Encoder(output).encode(bits, OCTET_STRING);
+   DER_Encoder(output).encode(bits, ASN1_Tag::OCTET_STRING);
    return output;
    }
 
@@ -57,7 +57,7 @@ AlgorithmIdentifier GOST_3410_PublicKey::algorithm_identifier() const
    const OID gost_oid = get_oid();
    const OID domain_oid = domain().get_curve_oid();
 
-   DER_Encoder(params).start_cons(SEQUENCE).encode(domain_oid).end_cons();
+   DER_Encoder(params).start_sequence().encode(domain_oid).end_cons();
 
    return AlgorithmIdentifier(gost_oid, params);
    }
@@ -68,7 +68,7 @@ GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
    OID ecc_param_id;
 
    // The parameters also includes hash and cipher OIDs
-   BER_Decoder(alg_id.get_parameters()).start_cons(SEQUENCE).decode(ecc_param_id);
+   BER_Decoder(alg_id.get_parameters()).start_sequence().decode(ecc_param_id);
 
    m_domain_params = EC_Group(ecc_param_id);
 
@@ -78,7 +78,7 @@ GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,
                            std::to_string(p_bits));
 
    secure_vector<uint8_t> bits;
-   BER_Decoder(key_bits).decode(bits, OCTET_STRING);
+   BER_Decoder(key_bits).decode(bits, ASN1_Tag::OCTET_STRING);
 
    const size_t part_size = bits.size() / 2;
 
diff --git a/src/lib/pubkey/mce/mceliece_key.cpp b/src/lib/pubkey/mce/mceliece_key.cpp
index f0bd8e61d..ed9654e0b 100644
--- a/src/lib/pubkey/mce/mceliece_key.cpp
+++ b/src/lib/pubkey/mce/mceliece_key.cpp
@@ -88,12 +88,12 @@ std::vector<uint8_t> McEliece_PublicKey::public_key_bits() const
    {
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
-         .start_cons(SEQUENCE)
+      .start_sequence()
+         .start_sequence()
          .encode(static_cast<size_t>(get_code_length()))
          .encode(static_cast<size_t>(get_t()))
          .end_cons()
-      .encode(m_public_matrix, OCTET_STRING)
+      .encode(m_public_matrix, ASN1_Tag::OCTET_STRING)
       .end_cons();
    return output;
    }
@@ -113,12 +113,12 @@ McEliece_PublicKey::McEliece_PublicKey(const std::vector<uint8_t>& key_bits)
    BER_Decoder dec(key_bits);
    size_t n;
    size_t t;
-   dec.start_cons(SEQUENCE)
-      .start_cons(SEQUENCE)
+   dec.start_sequence()
+      .start_sequence()
       .decode(n)
       .decode(t)
       .end_cons()
-      .decode(m_public_matrix, OCTET_STRING)
+      .decode(m_public_matrix, ASN1_Tag::OCTET_STRING)
       .end_cons();
    m_t = t;
    m_code_length = n;
@@ -127,17 +127,17 @@ McEliece_PublicKey::McEliece_PublicKey(const std::vector<uint8_t>& key_bits)
 secure_vector<uint8_t> McEliece_PrivateKey::private_key_bits() const
    {
    DER_Encoder enc;
-   enc.start_cons(SEQUENCE)
-      .start_cons(SEQUENCE)
+   enc.start_sequence()
+      .start_sequence()
       .encode(static_cast<size_t>(get_code_length()))
       .encode(static_cast<size_t>(get_t()))
       .end_cons()
-      .encode(m_public_matrix, OCTET_STRING)
-      .encode(m_g[0].encode(), OCTET_STRING); // g as octet string
-   enc.start_cons(SEQUENCE);
+      .encode(m_public_matrix, ASN1_Tag::OCTET_STRING)
+      .encode(m_g[0].encode(), ASN1_Tag::OCTET_STRING); // g as octet string
+   enc.start_sequence();
    for(size_t i = 0; i < m_sqrtmod.size(); i++)
       {
-      enc.encode(m_sqrtmod[i].encode(), OCTET_STRING);
+      enc.encode(m_sqrtmod[i].encode(), ASN1_Tag::OCTET_STRING);
       }
    enc.end_cons();
    secure_vector<uint8_t> enc_support;
@@ -147,7 +147,7 @@ secure_vector<uint8_t> McEliece_PrivateKey::private_key_bits() const
       enc_support.push_back(get_byte(0, Linv));
       enc_support.push_back(get_byte(1, Linv));
       }
-   enc.encode(enc_support, OCTET_STRING);
+   enc.encode(enc_support, ASN1_Tag::OCTET_STRING);
    secure_vector<uint8_t> enc_H;
    for(uint32_t coef : m_coeffs)
       {
@@ -156,7 +156,7 @@ secure_vector<uint8_t> McEliece_PrivateKey::private_key_bits() const
       enc_H.push_back(get_byte(2, coef));
       enc_H.push_back(get_byte(3, coef));
       }
-   enc.encode(enc_H, OCTET_STRING);
+   enc.encode(enc_H, ASN1_Tag::OCTET_STRING);
    enc.end_cons();
    return enc.get_contents();
    }
@@ -184,13 +184,13 @@ McEliece_PrivateKey::McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits)
    size_t n, t;
    secure_vector<uint8_t> enc_g;
    BER_Decoder dec_base(key_bits);
-   BER_Decoder dec = dec_base.start_cons(SEQUENCE)
-      .start_cons(SEQUENCE)
+   BER_Decoder dec = dec_base.start_sequence()
+      .start_sequence()
       .decode(n)
       .decode(t)
       .end_cons()
-      .decode(m_public_matrix, OCTET_STRING)
-      .decode(enc_g, OCTET_STRING);
+      .decode(m_public_matrix, ASN1_Tag::OCTET_STRING)
+      .decode(enc_g, ASN1_Tag::OCTET_STRING);
 
    if(t == 0 || n == 0)
       throw Decoding_Error("invalid McEliece parameters");
@@ -207,11 +207,11 @@ McEliece_PrivateKey::McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits)
       {
       throw Decoding_Error("degree of decoded Goppa polynomial is incorrect");
       }
-   BER_Decoder dec2 = dec.start_cons(SEQUENCE);
+   BER_Decoder dec2 = dec.start_sequence();
    for(uint32_t i = 0; i < t/2; i++)
       {
       secure_vector<uint8_t> sqrt_enc;
-      dec2.decode(sqrt_enc, OCTET_STRING);
+      dec2.decode(sqrt_enc, ASN1_Tag::OCTET_STRING);
       while(sqrt_enc.size() < (t*2))
          {
          // ensure that the length is always t
@@ -226,7 +226,7 @@ McEliece_PrivateKey::McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits)
       }
    secure_vector<uint8_t> enc_support;
    BER_Decoder dec3 = dec2.end_cons()
-      .decode(enc_support, OCTET_STRING);
+      .decode(enc_support, ASN1_Tag::OCTET_STRING);
    if(enc_support.size() % 2)
       {
       throw Decoding_Error("encoded support has odd length");
@@ -241,7 +241,7 @@ McEliece_PrivateKey::McEliece_PrivateKey(const secure_vector<uint8_t>& key_bits)
       m_Linv.push_back(el);
       }
    secure_vector<uint8_t> enc_H;
-   dec3.decode(enc_H, OCTET_STRING)
+   dec3.decode(enc_H, ASN1_Tag::OCTET_STRING)
       .end_cons();
    if(enc_H.size() % 4)
       {
diff --git a/src/lib/pubkey/pbes2/pbes2.cpp b/src/lib/pubkey/pbes2/pbes2.cpp
index 7a8846537..1b2da796b 100644
--- a/src/lib/pubkey/pbes2/pbes2.cpp
+++ b/src/lib/pubkey/pbes2/pbes2.cpp
@@ -41,11 +41,11 @@ SymmetricKey derive_key(const std::string& passphrase,
 
       AlgorithmIdentifier prf_algo;
       BER_Decoder(kdf_algo.get_parameters())
-         .start_cons(SEQUENCE)
-         .decode(salt, OCTET_STRING)
+         .start_sequence()
+         .decode(salt, ASN1_Tag::OCTET_STRING)
          .decode(iterations)
-         .decode_optional(key_length, INTEGER, UNIVERSAL)
-         .decode_optional(prf_algo, SEQUENCE, CONSTRUCTED,
+         .decode_optional(key_length, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL)
+         .decode_optional(prf_algo, ASN1_Tag::SEQUENCE, ASN1_Tag::CONSTRUCTED,
                           AlgorithmIdentifier("HMAC(SHA-160)",
                                               AlgorithmIdentifier::USE_NULL_PARAM))
          .end_cons();
@@ -69,12 +69,12 @@ SymmetricKey derive_key(const std::string& passphrase,
 
       AlgorithmIdentifier prf_algo;
       BER_Decoder(kdf_algo.get_parameters())
-         .start_cons(SEQUENCE)
-         .decode(salt, OCTET_STRING)
+         .start_sequence()
+         .decode(salt, ASN1_Tag::OCTET_STRING)
          .decode(N)
          .decode(r)
          .decode(p)
-         .decode_optional(key_length, INTEGER, UNIVERSAL)
+         .decode_optional(key_length, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL)
          .end_cons();
 
       if(key_length == 0)
@@ -134,8 +134,8 @@ secure_vector<uint8_t> derive_key(const std::string& passphrase,
 
       std::vector<uint8_t> scrypt_params;
       DER_Encoder(scrypt_params)
-         .start_cons(SEQUENCE)
-            .encode(salt, OCTET_STRING)
+         .start_sequence()
+            .encode(salt, ASN1_Tag::OCTET_STRING)
             .encode(N)
             .encode(r)
             .encode(p)
@@ -182,8 +182,8 @@ secure_vector<uint8_t> derive_key(const std::string& passphrase,
          *msec_in_iterations_out = iterations;
 
       DER_Encoder(pbkdf2_params)
-         .start_cons(SEQUENCE)
-            .encode(salt, OCTET_STRING)
+         .start_sequence()
+            .encode(salt, ASN1_Tag::OCTET_STRING)
             .encode(iterations)
             .encode(key_length)
             .encode_if(prf != "HMAC(SHA-160)",
@@ -240,11 +240,11 @@ pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits,
    enc->finish(ctext);
 
    std::vector<uint8_t> encoded_iv;
-   DER_Encoder(encoded_iv).encode(iv, OCTET_STRING);
+   DER_Encoder(encoded_iv).encode(iv, ASN1_Tag::OCTET_STRING);
 
    std::vector<uint8_t> pbes2_params;
    DER_Encoder(pbes2_params)
-      .start_cons(SEQUENCE)
+      .start_sequence()
       .encode(kdf_algo)
       .encode(AlgorithmIdentifier(cipher, encoded_iv))
       .end_cons();
@@ -307,7 +307,7 @@ pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
    AlgorithmIdentifier kdf_algo, enc_algo;
 
    BER_Decoder(params)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .decode(kdf_algo)
          .decode(enc_algo)
       .end_cons();
@@ -320,7 +320,7 @@ pbes2_decrypt(const secure_vector<uint8_t>& key_bits,
       throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher);
 
    secure_vector<uint8_t> iv;
-   BER_Decoder(enc_algo.get_parameters()).decode(iv, OCTET_STRING).verify_end();
+   BER_Decoder(enc_algo.get_parameters()).decode(iv, ASN1_Tag::OCTET_STRING).verify_end();
 
    std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION);
    if(!dec)
diff --git a/src/lib/pubkey/pk_keys.cpp b/src/lib/pubkey/pk_keys.cpp
index 54315a8b5..e1094663e 100644
--- a/src/lib/pubkey/pk_keys.cpp
+++ b/src/lib/pubkey/pk_keys.cpp
@@ -39,9 +39,9 @@ std::vector<uint8_t> Public_Key::subject_public_key() const
    {
    std::vector<uint8_t> output;
 
-   DER_Encoder(output).start_cons(SEQUENCE)
+   DER_Encoder(output).start_sequence()
          .encode(algorithm_identifier())
-         .encode(public_key_bits(), BIT_STRING)
+         .encode(public_key_bits(), ASN1_Tag::BIT_STRING)
       .end_cons();
 
    return output;
@@ -63,10 +63,10 @@ secure_vector<uint8_t> Private_Key::private_key_info() const
    const size_t PKCS8_VERSION = 0;
 
    return DER_Encoder()
-         .start_cons(SEQUENCE)
+         .start_sequence()
             .encode(PKCS8_VERSION)
             .encode(pkcs8_algorithm_identifier())
-            .encode(private_key_bits(), OCTET_STRING)
+            .encode(private_key_bits(), ASN1_Tag::OCTET_STRING)
          .end_cons()
       .get_contents();
    }
diff --git a/src/lib/pubkey/pkcs8.cpp b/src/lib/pubkey/pkcs8.cpp
index 7482b0bd9..fd210e6ce 100644
--- a/src/lib/pubkey/pkcs8.cpp
+++ b/src/lib/pubkey/pkcs8.cpp
@@ -34,9 +34,9 @@ secure_vector<uint8_t> PKCS8_extract(DataSource& source,
    secure_vector<uint8_t> key_data;
 
    BER_Decoder(source)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .decode(pbe_alg_id)
-         .decode(key_data, OCTET_STRING)
+         .decode(key_data, ASN1_Tag::OCTET_STRING)
       .verify_end();
 
    return key_data;
@@ -117,10 +117,10 @@ secure_vector<uint8_t> PKCS8_decode(
          key = key_data;
 
       BER_Decoder(key)
-         .start_cons(SEQUENCE)
+         .start_sequence()
             .decode_and_check<size_t>(0, "Unknown PKCS #8 version number")
             .decode(pk_alg_id)
-            .decode(key, OCTET_STRING)
+            .decode(key, ASN1_Tag::OCTET_STRING)
             .discard_remaining()
          .end_cons();
       }
@@ -203,9 +203,9 @@ std::vector<uint8_t> BER_encode(const Private_Key& key,
 
    std::vector<uint8_t> output;
    DER_Encoder der(output);
-   der.start_cons(SEQUENCE)
+   der.start_sequence()
          .encode(pbe_info.first)
-         .encode(pbe_info.second, OCTET_STRING)
+         .encode(pbe_info.second, ASN1_Tag::OCTET_STRING)
       .end_cons();
 
    return output;
@@ -251,9 +251,9 @@ std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
 
    std::vector<uint8_t> output;
    DER_Encoder der(output);
-   der.start_cons(SEQUENCE)
+   der.start_sequence()
          .encode(pbe_info.first)
-         .encode(pbe_info.second, OCTET_STRING)
+         .encode(pbe_info.second, ASN1_Tag::OCTET_STRING)
       .end_cons();
 
    return output;
@@ -300,9 +300,9 @@ std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
 
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode(pbe_info.first)
-         .encode(pbe_info.second, OCTET_STRING)
+         .encode(pbe_info.second, ASN1_Tag::OCTET_STRING)
       .end_cons();
 
    return output;
diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp
index a60195182..bfda7e4a6 100644
--- a/src/lib/pubkey/pubkey.cpp
+++ b/src/lib/pubkey/pubkey.cpp
@@ -255,7 +255,7 @@ std::vector<uint8_t> der_encode_signature(const std::vector<uint8_t>& sig,
 
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
       .encode_list(sig_parts)
       .end_cons();
    return output;
@@ -340,7 +340,7 @@ bool PK_Verifier::check_signature(const uint8_t sig[], size_t length)
          {
          std::vector<uint8_t> real_sig;
          BER_Decoder decoder(sig, length);
-         BER_Decoder ber_sig = decoder.start_cons(SEQUENCE);
+         BER_Decoder ber_sig = decoder.start_sequence();
 
          BOTAN_ASSERT_NOMSG(m_parts != 0 && m_part_size != 0);
 
diff --git a/src/lib/pubkey/rsa/rsa.cpp b/src/lib/pubkey/rsa/rsa.cpp
index 8899c9a63..717de0a67 100644
--- a/src/lib/pubkey/rsa/rsa.cpp
+++ b/src/lib/pubkey/rsa/rsa.cpp
@@ -120,7 +120,7 @@ RSA_PublicKey::RSA_PublicKey(const AlgorithmIdentifier&,
    {
    BigInt n, e;
    BER_Decoder(key_bits)
-      .start_cons(SEQUENCE)
+      .start_sequence()
       .decode(n)
       .decode(e)
       .end_cons();
@@ -154,7 +154,7 @@ std::vector<uint8_t> RSA_PublicKey::public_key_bits() const
    {
    std::vector<uint8_t> output;
    DER_Encoder der(output);
-   der.start_cons(SEQUENCE)
+   der.start_sequence()
          .encode(get_n())
          .encode(get_e())
       .end_cons();
@@ -180,7 +180,7 @@ std::shared_ptr<const RSA_Private_Data> RSA_PrivateKey::private_data() const
 secure_vector<uint8_t> RSA_PrivateKey::private_key_bits() const
    {
    return DER_Encoder()
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode(static_cast<size_t>(0))
          .encode(get_n())
          .encode(get_e())
@@ -214,7 +214,7 @@ RSA_PrivateKey::RSA_PrivateKey(const AlgorithmIdentifier&,
    BigInt n, e, d, p, q, d1, d2, c;
 
    BER_Decoder(key_bits)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .decode_and_check<size_t>(0, "Unknown PKCS #1 key format version")
          .decode(n)
          .decode(e)
diff --git a/src/lib/pubkey/sm2/sm2_enc.cpp b/src/lib/pubkey/sm2/sm2_enc.cpp
index 3b49f3177..6be10d416 100644
--- a/src/lib/pubkey/sm2/sm2_enc.cpp
+++ b/src/lib/pubkey/sm2/sm2_enc.cpp
@@ -91,11 +91,11 @@ class SM2_Encryption_Operation final : public PK_Ops::Encryption
          hash->final(C3.data());
 
          return DER_Encoder()
-            .start_cons(SEQUENCE)
+            .start_sequence()
             .encode(x1)
             .encode(y1)
-            .encode(C3, OCTET_STRING)
-            .encode(masked_msg, OCTET_STRING)
+            .encode(C3, ASN1_Tag::OCTET_STRING)
+            .encode(masked_msg, ASN1_Tag::OCTET_STRING)
             .end_cons()
             .get_contents();
          }
@@ -160,21 +160,21 @@ class SM2_Decryption_Operation final : public PK_Ops::Decryption
          secure_vector<uint8_t> C3, masked_msg;
 
          BER_Decoder(ciphertext, ciphertext_len)
-            .start_cons(SEQUENCE)
+            .start_sequence()
             .decode(x1)
             .decode(y1)
-            .decode(C3, OCTET_STRING)
-            .decode(masked_msg, OCTET_STRING)
+            .decode(C3, ASN1_Tag::OCTET_STRING)
+            .decode(masked_msg, ASN1_Tag::OCTET_STRING)
             .end_cons()
             .verify_end();
 
          std::vector<uint8_t> recode_ctext;
          DER_Encoder(recode_ctext)
-            .start_cons(SEQUENCE)
+            .start_sequence()
             .encode(x1)
             .encode(y1)
-            .encode(C3, OCTET_STRING)
-            .encode(masked_msg, OCTET_STRING)
+            .encode(C3, ASN1_Tag::OCTET_STRING)
+            .encode(masked_msg, ASN1_Tag::OCTET_STRING)
             .end_cons();
 
          if(recode_ctext.size() != ciphertext_len)
diff --git a/src/lib/pubkey/x509_key.cpp b/src/lib/pubkey/x509_key.cpp
index aeeeb2539..5ba689fc5 100644
--- a/src/lib/pubkey/x509_key.cpp
+++ b/src/lib/pubkey/x509_key.cpp
@@ -37,9 +37,9 @@ Public_Key* load_key(DataSource& source)
       if(ASN1::maybe_BER(source) && !PEM_Code::matches(source))
          {
          BER_Decoder(source)
-            .start_cons(SEQUENCE)
+            .start_sequence()
             .decode(alg_id)
-            .decode(key_bits, BIT_STRING)
+            .decode(key_bits, ASN1_Tag::BIT_STRING)
          .end_cons();
          }
       else
@@ -49,9 +49,9 @@ Public_Key* load_key(DataSource& source)
             );
 
          BER_Decoder(ber)
-            .start_cons(SEQUENCE)
+            .start_sequence()
             .decode(alg_id)
-            .decode(key_bits, BIT_STRING)
+            .decode(key_bits, ASN1_Tag::BIT_STRING)
          .end_cons();
          }
 
diff --git a/src/lib/pubkey/xmss/xmss_privatekey.cpp b/src/lib/pubkey/xmss/xmss_privatekey.cpp
index 5d2171266..ee5a15bdd 100644
--- a/src/lib/pubkey/xmss/xmss_privatekey.cpp
+++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp
@@ -37,7 +37,7 @@ secure_vector<uint8_t> extract_raw_key(const secure_vector<uint8_t>& key_bits)
    secure_vector<uint8_t> raw_key;
    try
       {
-      BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+      BER_Decoder(key_bits).decode(raw_key, ASN1_Tag::OCTET_STRING);
       }
    catch(Decoding_Error&)
       {
@@ -318,7 +318,7 @@ XMSS_PrivateKey::tree_hash_subtree(secure_vector<uint8_t>& result,
 
 secure_vector<uint8_t> XMSS_PrivateKey::private_key_bits() const
    {
-   return DER_Encoder().encode(raw_private_key(), OCTET_STRING).get_contents();
+   return DER_Encoder().encode(raw_private_key(), ASN1_Tag::OCTET_STRING).get_contents();
    }
 
 std::shared_ptr<Atomic<size_t>>
diff --git a/src/lib/pubkey/xmss/xmss_publickey.cpp b/src/lib/pubkey/xmss/xmss_publickey.cpp
index f6db6d502..0ec34cde0 100644
--- a/src/lib/pubkey/xmss/xmss_publickey.cpp
+++ b/src/lib/pubkey/xmss/xmss_publickey.cpp
@@ -30,7 +30,7 @@ std::vector<uint8_t> extract_raw_key(const std::vector<uint8_t>& key_bits)
    std::vector<uint8_t> raw_key;
    try
       {
-      BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+      BER_Decoder(key_bits).decode(raw_key, ASN1_Tag::OCTET_STRING);
       }
    catch(Decoding_Error&)
       {
@@ -122,7 +122,7 @@ std::vector<uint8_t> XMSS_PublicKey::raw_public_key() const
 std::vector<uint8_t> XMSS_PublicKey::public_key_bits() const
    {
    std::vector<uint8_t> output;
-   DER_Encoder(output).encode(raw_public_key(), OCTET_STRING);
+   DER_Encoder(output).encode(raw_public_key(), ASN1_Tag::OCTET_STRING);
    return output;
    }
 
diff --git a/src/lib/rng/system_rng/system_rng.cpp b/src/lib/rng/system_rng/system_rng.cpp
index 2a783fd14..2b4a21c80 100644
--- a/src/lib/rng/system_rng/system_rng.cpp
+++ b/src/lib/rng/system_rng/system_rng.cpp
@@ -59,10 +59,7 @@ class System_RNG_Impl final : public RandomNumberGenerator
       void clear() override { /* not possible */ }
       std::string name() const override { return "RtlGenRandom"; }
    private:
-      // Use type BYTE instead of BOOLEAN because of a naming conflict
-      // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx
-      // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
-      using RtlGenRandom_fptr = BYTE (NTAPI *)(PVOID, ULONG);
+      using RtlGenRandom_fptr = BOOLEAN (NTAPI *)(PVOID, ULONG);
 
       Dynamically_Loaded_Library m_advapi;
       RtlGenRandom_fptr m_rtlgenrandom;
diff --git a/src/lib/tls/tls_session.cpp b/src/lib/tls/tls_session.cpp
index de118f778..4df079655 100644
--- a/src/lib/tls/tls_session.cpp
+++ b/src/lib/tls/tls_session.cpp
@@ -71,22 +71,22 @@ Session::Session(const uint8_t ber[], size_t ber_len)
    size_t compression_method = 0;
 
    BER_Decoder(ber, ber_len)
-      .start_cons(SEQUENCE)
+      .start_sequence()
         .decode_and_check(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION),
                           "Unknown version in serialized TLS session")
         .decode_integer_type(start_time)
         .decode_integer_type(major_version)
         .decode_integer_type(minor_version)
-        .decode(m_identifier, OCTET_STRING)
-        .decode(m_session_ticket, OCTET_STRING)
+        .decode(m_identifier, ASN1_Tag::OCTET_STRING)
+        .decode(m_session_ticket, ASN1_Tag::OCTET_STRING)
         .decode_integer_type(m_ciphersuite)
         .decode_integer_type(compression_method)
         .decode_integer_type(side_code)
         .decode_integer_type(fragment_size)
         .decode(m_extended_master_secret)
         .decode(m_encrypt_then_mac)
-        .decode(m_master_secret, OCTET_STRING)
-        .decode(peer_cert_bits, OCTET_STRING)
+        .decode(m_master_secret, ASN1_Tag::OCTET_STRING)
+        .decode(peer_cert_bits, ASN1_Tag::OCTET_STRING)
         .decode(server_hostname)
         .decode(server_service)
         .decode(server_port)
@@ -138,25 +138,25 @@ secure_vector<uint8_t> Session::DER_encode() const
       peer_cert_bits += m_peer_certs[i].BER_encode();
 
    return DER_Encoder()
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode(static_cast<size_t>(TLS_SESSION_PARAM_STRUCT_VERSION))
          .encode(static_cast<size_t>(std::chrono::system_clock::to_time_t(m_start_time)))
          .encode(static_cast<size_t>(m_version.major_version()))
          .encode(static_cast<size_t>(m_version.minor_version()))
-         .encode(m_identifier, OCTET_STRING)
-         .encode(m_session_ticket, OCTET_STRING)
+         .encode(m_identifier, ASN1_Tag::OCTET_STRING)
+         .encode(m_session_ticket, ASN1_Tag::OCTET_STRING)
          .encode(static_cast<size_t>(m_ciphersuite))
          .encode(static_cast<size_t>(/*old compression method*/0))
          .encode(static_cast<size_t>(m_connection_side))
          .encode(static_cast<size_t>(/*old fragment size*/0))
          .encode(m_extended_master_secret)
          .encode(m_encrypt_then_mac)
-         .encode(m_master_secret, OCTET_STRING)
-         .encode(peer_cert_bits, OCTET_STRING)
-         .encode(ASN1_String(m_server_info.hostname(), UTF8_STRING))
-         .encode(ASN1_String(m_server_info.service(), UTF8_STRING))
+         .encode(m_master_secret, ASN1_Tag::OCTET_STRING)
+         .encode(peer_cert_bits, ASN1_Tag::OCTET_STRING)
+         .encode(ASN1_String(m_server_info.hostname(), ASN1_Tag::UTF8_STRING))
+         .encode(ASN1_String(m_server_info.service(), ASN1_Tag::UTF8_STRING))
          .encode(static_cast<size_t>(m_server_info.port()))
-         .encode(ASN1_String("", UTF8_STRING)) // old srp identifier
+         .encode(ASN1_String("", ASN1_Tag::UTF8_STRING)) // old srp identifier
          .encode(static_cast<size_t>(m_srtp_profile))
       .end_cons()
    .get_contents();
diff --git a/src/lib/x509/asn1_alt_name.cpp b/src/lib/x509/asn1_alt_name.cpp
index 9d41fa326..8fa67a57b 100644
--- a/src/lib/x509/asn1_alt_name.cpp
+++ b/src/lib/x509/asn1_alt_name.cpp
@@ -141,15 +141,15 @@ void encode_entries(DER_Encoder& encoder,
       {
       if(type == "RFC822" || type == "DNS" || type == "URI")
          {
-         ASN1_String asn1_string(i->second, IA5_STRING);
-         encoder.add_object(tagging, CONTEXT_SPECIFIC, asn1_string.value());
+         ASN1_String asn1_string(i->second, ASN1_Tag::IA5_STRING);
+         encoder.add_object(tagging, ASN1_Tag::CONTEXT_SPECIFIC, asn1_string.value());
          }
       else if(type == "IP")
          {
          const uint32_t ip = string_to_ipv4(i->second);
          uint8_t ip_buf[4] = { 0 };
          store_be(ip, ip_buf);
-         encoder.add_object(tagging, CONTEXT_SPECIFIC, ip_buf, 4);
+         encoder.add_object(tagging, ASN1_Tag::CONTEXT_SPECIFIC, ip_buf, 4);
          }
       else if (type == "DN")
          {
@@ -168,7 +168,7 @@ void encode_entries(DER_Encoder& encoder,
 */
 void AlternativeName::encode_into(DER_Encoder& der) const
    {
-   der.start_cons(SEQUENCE);
+   der.start_sequence();
 
    encode_entries(der, m_alt_info, "RFC822", ASN1_Tag(1));
    encode_entries(der, m_alt_info, "DNS", ASN1_Tag(2));
@@ -194,7 +194,7 @@ void AlternativeName::encode_into(DER_Encoder& der) const
 */
 void AlternativeName::decode_from(BER_Decoder& source)
    {
-   BER_Decoder names = source.start_cons(SEQUENCE);
+   BER_Decoder names = source.start_sequence();
 
    // FIXME this is largely a duplication of GeneralName::decode_from
 
@@ -202,7 +202,7 @@ void AlternativeName::decode_from(BER_Decoder& source)
       {
       BER_Object obj = names.get_next_object();
 
-      if(obj.is_a(0, CONTEXT_SPECIFIC))
+      if(obj.is_a(0, ASN1_Tag::CONTEXT_SPECIFIC))
          {
          BER_Decoder othername(obj);
 
@@ -213,7 +213,7 @@ void AlternativeName::decode_from(BER_Decoder& source)
             BER_Object othername_value_outer = othername.get_next_object();
             othername.verify_end();
 
-            if(othername_value_outer.is_a(0, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+            if(othername_value_outer.is_a(0, ASN1_Tag::PRIVATE) == false)
                throw Decoding_Error("Invalid tags on otherName value");
 
             BER_Decoder othername_value_inner(othername_value_outer);
@@ -221,25 +221,25 @@ void AlternativeName::decode_from(BER_Decoder& source)
             BER_Object value = othername_value_inner.get_next_object();
             othername_value_inner.verify_end();
 
-            if(ASN1_String::is_string_type(value.type()) && value.get_class() == UNIVERSAL)
+            if(ASN1_String::is_string_type(value.type()) && value.get_class() == ASN1_Tag::UNIVERSAL)
                {
                add_othername(oid, ASN1::to_string(value), value.type());
                }
             }
          }
-      if(obj.is_a(1, CONTEXT_SPECIFIC))
+      if(obj.is_a(1, ASN1_Tag::CONTEXT_SPECIFIC))
          {
          add_attribute("RFC822", ASN1::to_string(obj));
          }
-      else if(obj.is_a(2, CONTEXT_SPECIFIC))
+      else if(obj.is_a(2, ASN1_Tag::CONTEXT_SPECIFIC))
          {
          add_attribute("DNS", ASN1::to_string(obj));
          }
-      else if(obj.is_a(6, CONTEXT_SPECIFIC))
+      else if(obj.is_a(6, ASN1_Tag::CONTEXT_SPECIFIC))
          {
          add_attribute("URI", ASN1::to_string(obj));
          }
-      else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+      else if(obj.is_a(4, ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED))
          {
          BER_Decoder dec(obj);
          X509_DN dn;
@@ -250,7 +250,7 @@ void AlternativeName::decode_from(BER_Decoder& source)
 
          add_attribute("DN", ss.str());
          }
-      else if(obj.is_a(7, CONTEXT_SPECIFIC))
+      else if(obj.is_a(7, ASN1_Tag::CONTEXT_SPECIFIC))
          {
          if(obj.length() == 4)
             {
diff --git a/src/lib/x509/crl_ent.cpp b/src/lib/x509/crl_ent.cpp
index def7b87f2..f0abb9c82 100644
--- a/src/lib/x509/crl_ent.cpp
+++ b/src/lib/x509/crl_ent.cpp
@@ -65,10 +65,10 @@ bool operator!=(const CRL_Entry& a1, const CRL_Entry& a2)
 */
 void CRL_Entry::encode_into(DER_Encoder& der) const
    {
-   der.start_cons(SEQUENCE)
+   der.start_sequence()
       .encode(BigInt::decode(serial_number()))
       .encode(expire_time())
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode(extensions())
       .end_cons()
    .end_cons();
@@ -83,7 +83,7 @@ void CRL_Entry::decode_from(BER_Decoder& source)
 
    std::unique_ptr<CRL_Entry_Data> data(new CRL_Entry_Data);
 
-   BER_Decoder entry = source.start_cons(SEQUENCE);
+   BER_Decoder entry = source.start_sequence();
 
    entry.decode(serial_number_bn).decode(data->m_time);
    data->m_serial = BigInt::encode(serial_number_bn);
diff --git a/src/lib/x509/name_constraint.cpp b/src/lib/x509/name_constraint.cpp
index 6d650c6be..20f588411 100644
--- a/src/lib/x509/name_constraint.cpp
+++ b/src/lib/x509/name_constraint.cpp
@@ -41,22 +41,22 @@ void GeneralName::decode_from(class BER_Decoder& ber)
    {
    BER_Object obj = ber.get_next_object();
 
-   if(obj.is_a(1, CONTEXT_SPECIFIC))
+   if(obj.is_a(1, ASN1_Tag::CONTEXT_SPECIFIC))
       {
       m_type = "RFC822";
       m_name = ASN1::to_string(obj);
       }
-   else if(obj.is_a(2, CONTEXT_SPECIFIC))
+   else if(obj.is_a(2, ASN1_Tag::CONTEXT_SPECIFIC))
       {
       m_type = "DNS";
       m_name = ASN1::to_string(obj);
       }
-   else if(obj.is_a(6, CONTEXT_SPECIFIC))
+   else if(obj.is_a(6, ASN1_Tag::CONTEXT_SPECIFIC))
       {
       m_type = "URI";
       m_name = ASN1::to_string(obj);
       }
-   else if(obj.is_a(4, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)))
+   else if(obj.is_a(4, ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED))
       {
       m_type = "DN";
       X509_DN dn;
@@ -68,7 +68,7 @@ void GeneralName::decode_from(class BER_Decoder& ber)
 
       m_name = ss.str();
       }
-   else if(obj.is_a(7, CONTEXT_SPECIFIC))
+   else if(obj.is_a(7, ASN1_Tag::CONTEXT_SPECIFIC))
       {
       if(obj.length() == 8)
          {
@@ -253,9 +253,9 @@ void GeneralSubtree::encode_into(DER_Encoder&) const
 
 void GeneralSubtree::decode_from(class BER_Decoder& ber)
    {
-   ber.start_cons(SEQUENCE)
+   ber.start_sequence()
       .decode(m_base)
-      .decode_optional(m_minimum,ASN1_Tag(0), CONTEXT_SPECIFIC,size_t(0))
+      .decode_optional(m_minimum, ASN1_Tag(0), ASN1_Tag::CONTEXT_SPECIFIC, size_t(0))
    .end_cons();
 
    if(m_minimum != 0)
diff --git a/src/lib/x509/ocsp.cpp b/src/lib/x509/ocsp.cpp
index ec120764a..71d4e499c 100644
--- a/src/lib/x509/ocsp.cpp
+++ b/src/lib/x509/ocsp.cpp
@@ -32,7 +32,7 @@ void decode_optional_list(BER_Decoder& ber,
    {
    BER_Object obj = ber.get_next_object();
 
-   if(obj.is_a(tag, ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED)) == false)
+   if(obj.is_a(tag, ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED) == false)
       {
       ber.push_back(obj);
       return;
@@ -69,13 +69,13 @@ Request::Request(const X509_Certificate& issuer_cert,
 std::vector<uint8_t> Request::BER_encode() const
    {
    std::vector<uint8_t> output;
-   DER_Encoder(output).start_cons(SEQUENCE)
-        .start_cons(SEQUENCE)
+   DER_Encoder(output).start_sequence()
+        .start_sequence()
           .start_explicit(0)
             .encode(static_cast<size_t>(0)) // version #
           .end_explicit()
-            .start_cons(SEQUENCE)
-              .start_cons(SEQUENCE)
+            .start_sequence()
+              .start_sequence()
                 .encode(m_certid)
               .end_cons()
             .end_cons()
@@ -101,11 +101,11 @@ Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
    {
    m_dummy_response_status = Certificate_Status_Code::OCSP_RESPONSE_INVALID;
 
-   BER_Decoder response_outer = BER_Decoder(m_response_bits).start_cons(SEQUENCE);
+   BER_Decoder response_outer = BER_Decoder(m_response_bits).start_sequence();
 
    size_t resp_status = 0;
 
-   response_outer.decode(resp_status, ENUMERATED, UNIVERSAL);
+   response_outer.decode(resp_status, ASN1_Tag::ENUMERATED, ASN1_Tag::UNIVERSAL);
 
    m_status = static_cast<Response_Status_Code>(resp_status);
 
@@ -115,19 +115,19 @@ Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
    if(response_outer.more_items())
       {
       BER_Decoder response_bytes =
-         response_outer.start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC).start_cons(SEQUENCE);
+         response_outer.start_cons(ASN1_Tag(0), ASN1_Tag::CONTEXT_SPECIFIC).start_sequence();
 
       response_bytes.decode_and_check(OID("1.3.6.1.5.5.7.48.1.1"),
                                       "Unknown response type in OCSP response");
 
       BER_Decoder basicresponse =
-         BER_Decoder(response_bytes.get_next_octet_string()).start_cons(SEQUENCE);
+         BER_Decoder(response_bytes.get_next_octet_string()).start_sequence();
 
-      basicresponse.start_cons(SEQUENCE)
+      basicresponse.start_sequence()
            .raw_bytes(m_tbs_bits)
          .end_cons()
          .decode(m_sig_algo)
-         .decode(m_signature, BIT_STRING);
+         .decode(m_signature, ASN1_Tag::BIT_STRING);
       decode_optional_list(basicresponse, ASN1_Tag(0), m_certs);
 
       size_t responsedata_version = 0;
@@ -135,20 +135,20 @@ Response::Response(const uint8_t response_bits[], size_t response_bits_len) :
 
       BER_Decoder(m_tbs_bits)
          .decode_optional(responsedata_version, ASN1_Tag(0),
-                          ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+                          ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED)
 
          .decode_optional(m_signer_name, ASN1_Tag(1),
-                          ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+                          ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED)
 
-         .decode_optional_string(m_key_hash, OCTET_STRING, 2,
-                                 ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+         .decode_optional_string(m_key_hash, ASN1_Tag::OCTET_STRING, 2,
+                                 ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED)
 
          .decode(m_produced_at)
 
          .decode_list(m_responses)
 
          .decode_optional(extensions, ASN1_Tag(1),
-                          ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+                          ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED);
       }
 
    response_outer.end_cons();
diff --git a/src/lib/x509/ocsp_types.cpp b/src/lib/x509/ocsp_types.cpp
index c4b78112e..c03261b59 100644
--- a/src/lib/x509/ocsp_types.cpp
+++ b/src/lib/x509/ocsp_types.cpp
@@ -57,20 +57,20 @@ bool CertID::is_id_for(const X509_Certificate& issuer,
 
 void CertID::encode_into(class DER_Encoder& to) const
    {
-   to.start_cons(SEQUENCE)
+   to.start_sequence()
       .encode(m_hash_id)
-      .encode(m_issuer_dn_hash, OCTET_STRING)
-      .encode(m_issuer_key_hash, OCTET_STRING)
+      .encode(m_issuer_dn_hash, ASN1_Tag::OCTET_STRING)
+      .encode(m_issuer_key_hash, ASN1_Tag::OCTET_STRING)
       .encode(m_subject_serial)
       .end_cons();
    }
 
 void CertID::decode_from(class BER_Decoder& from)
    {
-   from.start_cons(SEQUENCE)
+   from.start_sequence()
       .decode(m_hash_id)
-      .decode(m_issuer_dn_hash, OCTET_STRING)
-      .decode(m_issuer_key_hash, OCTET_STRING)
+      .decode(m_issuer_dn_hash, ASN1_Tag::OCTET_STRING)
+      .decode(m_issuer_key_hash, ASN1_Tag::OCTET_STRING)
       .decode(m_subject_serial)
       .end_cons();
 
@@ -86,18 +86,31 @@ void SingleResponse::decode_from(class BER_Decoder& from)
    BER_Object cert_status;
    Extensions extensions;
 
-   from.start_cons(SEQUENCE)
+   from.start_sequence()
       .decode(m_certid)
       .get_next(cert_status)
       .decode(m_thisupdate)
       .decode_optional(m_nextupdate, ASN1_Tag(0),
-                       ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+                       ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED)
       .decode_optional(extensions,
                        ASN1_Tag(1),
-                       ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED))
+                       ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED)
       .end_cons();
 
-   m_cert_status = cert_status.type();
+   /* CertStatus ::= CHOICE {
+       good        [0]     IMPLICIT NULL,
+       revoked     [1]     IMPLICIT RevokedInfo,
+       unknown     [2]     IMPLICIT UnknownInfo }
+
+   RevokedInfo ::= SEQUENCE {
+       revocationTime              GeneralizedTime,
+       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+
+   UnknownInfo ::= NULL
+
+   We should verify the expected body and decode the RevokedInfo
+   */
+   m_cert_status = static_cast<uint32_t>(cert_status.type());
    }
 
 }
diff --git a/src/lib/x509/pkcs10.cpp b/src/lib/x509/pkcs10.cpp
index 1da5ecca4..274ded692 100644
--- a/src/lib/x509/pkcs10.cpp
+++ b/src/lib/x509/pkcs10.cpp
@@ -71,7 +71,7 @@ PKCS10_Request PKCS10_Request::create(const Private_Key& key,
 
    DER_Encoder tbs_req;
 
-   tbs_req.start_cons(SEQUENCE)
+   tbs_req.start_sequence()
       .encode(PKCS10_VERSION)
       .encode(subject_dn)
       .raw_bytes(key.subject_public_key())
@@ -80,12 +80,12 @@ PKCS10_Request PKCS10_Request::create(const Private_Key& key,
    if(challenge.empty() == false)
       {
       std::vector<uint8_t> value;
-      DER_Encoder(value).encode(ASN1_String(challenge, DIRECTORY_STRING));
+      DER_Encoder(value).encode(ASN1_String(challenge, ASN1_Tag::DIRECTORY_STRING));
       tbs_req.encode(Attribute("PKCS9.ChallengePassword", value));
       }
 
    std::vector<uint8_t> extension_req;
-   DER_Encoder(extension_req).start_cons(SEQUENCE).encode(extensions).end_cons();
+   DER_Encoder(extension_req).start_sequence().encode(extensions).end_cons();
    tbs_req.encode(Attribute("PKCS9.ExtensionRequest", extension_req));
 
    // end the start_explicit above
@@ -118,7 +118,7 @@ std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
    cert_req_info.decode(data->m_subject_dn);
 
    BER_Object public_key = cert_req_info.get_next_object();
-   if(public_key.is_a(SEQUENCE, CONSTRUCTED) == false)
+   if(public_key.is_a(ASN1_Tag::SEQUENCE, ASN1_Tag::CONSTRUCTED) == false)
       throw BER_Bad_Tag("PKCS10_Request: Unexpected tag for public key", public_key.tagging());
 
    data->m_public_key_bits = ASN1::put_in_sequence(public_key.bits(), public_key.length());
@@ -127,7 +127,7 @@ std::unique_ptr<PKCS10_Data> decode_pkcs10(const std::vector<uint8_t>& body)
 
    std::set<std::string> pkcs9_email;
 
-   if(attr_bits.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+   if(attr_bits.is_a(0, ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC))
       {
       BER_Decoder attributes(attr_bits);
       while(attributes.more_items())
diff --git a/src/lib/x509/x509_attribute.cpp b/src/lib/x509/x509_attribute.cpp
index 4459531d0..2f247e7f3 100644
--- a/src/lib/x509/x509_attribute.cpp
+++ b/src/lib/x509/x509_attribute.cpp
@@ -34,9 +34,9 @@ Attribute::Attribute(const std::string& attr_oid,
 */
 void Attribute::encode_into(DER_Encoder& codec) const
    {
-   codec.start_cons(SEQUENCE)
+   codec.start_sequence()
       .encode(m_oid)
-      .start_cons(SET)
+      .start_cons(ASN1_Tag::SET)
          .raw_bytes(m_parameters)
       .end_cons()
    .end_cons();
@@ -47,9 +47,9 @@ void Attribute::encode_into(DER_Encoder& codec) const
 */
 void Attribute::decode_from(BER_Decoder& codec)
    {
-   codec.start_cons(SEQUENCE)
+   codec.start_sequence()
       .decode(m_oid)
-      .start_cons(SET)
+      .start_cons(ASN1_Tag::SET)
          .raw_bytes(m_parameters)
       .end_cons()
    .end_cons();
diff --git a/src/lib/x509/x509_ca.cpp b/src/lib/x509/x509_ca.cpp
index 63207b336..67e17be33 100644
--- a/src/lib/x509/x509_ca.cpp
+++ b/src/lib/x509/x509_ca.cpp
@@ -183,7 +183,7 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer,
    // clang-format off
    return X509_Certificate(X509_Object::make_signed(
       signer, rng, sig_algo,
-      DER_Encoder().start_cons(SEQUENCE)
+      DER_Encoder().start_sequence()
          .start_explicit(0)
             .encode(X509_CERT_VERSION-1)
          .end_explicit()
@@ -193,7 +193,7 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer,
          .encode(sig_algo)
          .encode(issuer_dn)
 
-         .start_cons(SEQUENCE)
+         .start_sequence()
             .encode(not_before)
             .encode(not_after)
          .end_cons()
@@ -202,7 +202,7 @@ X509_Certificate X509_CA::make_cert(PK_Signer* signer,
          .raw_bytes(pub_key)
 
          .start_explicit(3)
-            .start_cons(SEQUENCE)
+            .start_sequence()
                .encode(extensions)
              .end_cons()
          .end_explicit()
@@ -279,7 +279,7 @@ X509_CRL X509_CA::make_crl(const std::vector<CRL_Entry>& revoked,
    // clang-format off
    const std::vector<uint8_t> crl = X509_Object::make_signed(
       m_signer.get(), rng, m_ca_sig_algo,
-      DER_Encoder().start_cons(SEQUENCE)
+      DER_Encoder().start_sequence()
          .encode(X509_CRL_VERSION-1)
          .encode(m_ca_sig_algo)
          .encode(m_ca_cert.subject_dn())
@@ -287,12 +287,12 @@ X509_CRL X509_CA::make_crl(const std::vector<CRL_Entry>& revoked,
          .encode(X509_Time(expire_time))
          .encode_if(revoked.size() > 0,
               DER_Encoder()
-                 .start_cons(SEQUENCE)
+                 .start_sequence()
                     .encode_list(revoked)
                  .end_cons()
             )
          .start_explicit(0)
-            .start_cons(SEQUENCE)
+            .start_sequence()
                .encode(extensions)
             .end_cons()
          .end_explicit()
diff --git a/src/lib/x509/x509_crl.cpp b/src/lib/x509/x509_crl.cpp
index 5f7484d5b..a0368c13c 100644
--- a/src/lib/x509/x509_crl.cpp
+++ b/src/lib/x509/x509_crl.cpp
@@ -123,7 +123,7 @@ std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
    BER_Decoder tbs_crl(body);
 
    size_t version;
-   tbs_crl.decode_optional(version, INTEGER, UNIVERSAL);
+   tbs_crl.decode_optional(version, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL);
 
    if(version != 0 && version != 1)
       throw Decoding_Error("Unknown X.509 CRL version " + std::to_string(version+1));
@@ -140,7 +140,7 @@ std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
 
    BER_Object next = tbs_crl.get_next_object();
 
-   if(next.is_a(SEQUENCE, CONSTRUCTED))
+   if(next.is_a(ASN1_Tag::SEQUENCE, ASN1_Tag::CONSTRUCTED))
       {
       BER_Decoder cert_list(std::move(next));
 
@@ -153,7 +153,7 @@ std::unique_ptr<CRL_Data> decode_crl_body(const std::vector<uint8_t>& body,
       next = tbs_crl.get_next_object();
       }
 
-   if(next.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+   if(next.is_a(0, ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC))
       {
       BER_Decoder crl_options(std::move(next));
       crl_options.decode(data->m_extensions).verify_end();
diff --git a/src/lib/x509/x509_dn.cpp b/src/lib/x509/x509_dn.cpp
index d0ca99228..2fb2088d4 100644
--- a/src/lib/x509/x509_dn.cpp
+++ b/src/lib/x509/x509_dn.cpp
@@ -248,7 +248,7 @@ std::vector<uint8_t> X509_DN::DER_encode() const
 */
 void X509_DN::encode_into(DER_Encoder& der) const
    {
-   der.start_cons(SEQUENCE);
+   der.start_sequence();
 
    if(!m_dn_bits.empty())
       {
@@ -262,8 +262,8 @@ void X509_DN::encode_into(DER_Encoder& der) const
       {
       for(const auto& dn : m_rdn)
          {
-         der.start_cons(SET)
-            .start_cons(SEQUENCE)
+         der.start_cons(ASN1_Tag::SET)
+            .start_sequence()
             .encode(dn.first)
             .encode(dn.second)
             .end_cons()
@@ -281,7 +281,7 @@ void X509_DN::decode_from(BER_Decoder& source)
    {
    std::vector<uint8_t> bits;
 
-   source.start_cons(SEQUENCE)
+   source.start_sequence()
       .raw_bytes(bits)
    .end_cons();
 
@@ -291,14 +291,14 @@ void X509_DN::decode_from(BER_Decoder& source)
 
    while(sequence.more_items())
       {
-      BER_Decoder rdn = sequence.start_cons(SET);
+      BER_Decoder rdn = sequence.start_cons(ASN1_Tag::SET);
 
       while(rdn.more_items())
          {
          OID oid;
          ASN1_String str;
 
-         rdn.start_cons(SEQUENCE)
+         rdn.start_sequence()
             .decode(oid)
             .decode(str) // TODO support Any
             .end_cons().verify_end("Invalid X509_DN, data follows RDN");
diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp
index e000b2f11..85124e982 100644
--- a/src/lib/x509/x509_ext.cpp
+++ b/src/lib/x509/x509_ext.cpp
@@ -254,10 +254,10 @@ void Extensions::encode_into(DER_Encoder& to_object) const
          const bool is_critical = ext_info.second.is_critical();
          const std::vector<uint8_t>& ext_value = ext_info.second.bits();
 
-         to_object.start_cons(SEQUENCE)
+         to_object.start_sequence()
                .encode(oid)
                .encode_optional(is_critical, false)
-               .encode(ext_value, OCTET_STRING)
+               .encode(ext_value, ASN1_Tag::OCTET_STRING)
             .end_cons();
          }
       }
@@ -271,7 +271,7 @@ void Extensions::decode_from(BER_Decoder& from_source)
    m_extension_oids.clear();
    m_extension_info.clear();
 
-   BER_Decoder sequence = from_source.start_cons(SEQUENCE);
+   BER_Decoder sequence = from_source.start_sequence();
 
    while(sequence.more_items())
       {
@@ -279,10 +279,10 @@ void Extensions::decode_from(BER_Decoder& from_source)
       bool critical;
       std::vector<uint8_t> bits;
 
-      sequence.start_cons(SEQUENCE)
+      sequence.start_sequence()
          .decode(oid)
-         .decode_optional(critical, BOOLEAN, UNIVERSAL, false)
-         .decode(bits, OCTET_STRING)
+         .decode_optional(critical, ASN1_Tag::BOOLEAN, ASN1_Tag::UNIVERSAL, false)
+         .decode(bits, ASN1_Tag::OCTET_STRING)
       .end_cons();
 
       std::unique_ptr<Certificate_Extension> obj = create_extn_obj(oid, critical, bits);
@@ -313,7 +313,7 @@ std::vector<uint8_t> Basic_Constraints::encode_inner() const
    {
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
       .encode_if(m_is_ca,
                  DER_Encoder()
                     .encode(m_is_ca)
@@ -329,9 +329,9 @@ std::vector<uint8_t> Basic_Constraints::encode_inner() const
 void Basic_Constraints::decode_inner(const std::vector<uint8_t>& in)
    {
    BER_Decoder(in)
-      .start_cons(SEQUENCE)
-         .decode_optional(m_is_ca, BOOLEAN, UNIVERSAL, false)
-         .decode_optional(m_path_limit, INTEGER, UNIVERSAL, NO_CERT_PATH_LIMIT)
+      .start_sequence()
+         .decode_optional(m_is_ca, ASN1_Tag::BOOLEAN, ASN1_Tag::UNIVERSAL, false)
+         .decode_optional(m_path_limit, ASN1_Tag::INTEGER, ASN1_Tag::UNIVERSAL, NO_CERT_PATH_LIMIT)
       .end_cons();
 
    if(m_is_ca == false)
@@ -349,7 +349,7 @@ std::vector<uint8_t> Key_Usage::encode_inner() const
    const size_t unused_bits = ctz(static_cast<uint32_t>(m_constraints));
 
    std::vector<uint8_t> der;
-   der.push_back(BIT_STRING);
+   der.push_back(static_cast<uint8_t>(ASN1_Tag::BIT_STRING));
    der.push_back(2 + ((unused_bits < 8) ? 1 : 0));
    der.push_back(unused_bits % 8);
    der.push_back((m_constraints >> 8) & 0xFF);
@@ -368,7 +368,7 @@ void Key_Usage::decode_inner(const std::vector<uint8_t>& in)
 
    BER_Object obj = ber.get_next_object();
 
-   obj.assert_is_a(BIT_STRING, UNIVERSAL, "usage constraint");
+   obj.assert_is_a(ASN1_Tag::BIT_STRING, ASN1_Tag::UNIVERSAL, "usage constraint");
 
    if(obj.length() != 2 && obj.length() != 3)
       throw BER_Decoding_Error("Bad size for BITSTRING in usage constraint");
@@ -400,7 +400,7 @@ void Key_Usage::decode_inner(const std::vector<uint8_t>& in)
 std::vector<uint8_t> Subject_Key_ID::encode_inner() const
    {
    std::vector<uint8_t> output;
-   DER_Encoder(output).encode(m_key_id, OCTET_STRING);
+   DER_Encoder(output).encode(m_key_id, ASN1_Tag::OCTET_STRING);
    return output;
    }
 
@@ -409,7 +409,7 @@ std::vector<uint8_t> Subject_Key_ID::encode_inner() const
 */
 void Subject_Key_ID::decode_inner(const std::vector<uint8_t>& in)
    {
-   BER_Decoder(in).decode(m_key_id, OCTET_STRING).verify_end();
+   BER_Decoder(in).decode(m_key_id, ASN1_Tag::OCTET_STRING).verify_end();
    }
 
 /*
@@ -437,8 +437,8 @@ std::vector<uint8_t> Authority_Key_ID::encode_inner() const
    {
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
-         .encode(m_key_id, OCTET_STRING, ASN1_Tag(0), CONTEXT_SPECIFIC)
+      .start_sequence()
+         .encode(m_key_id, ASN1_Tag::OCTET_STRING, ASN1_Tag(0), ASN1_Tag::CONTEXT_SPECIFIC)
       .end_cons();
    return output;
    }
@@ -449,8 +449,8 @@ std::vector<uint8_t> Authority_Key_ID::encode_inner() const
 void Authority_Key_ID::decode_inner(const std::vector<uint8_t>& in)
    {
    BER_Decoder(in)
-      .start_cons(SEQUENCE)
-      .decode_optional_string(m_key_id, OCTET_STRING, 0);
+      .start_sequence()
+      .decode_optional_string(m_key_id, ASN1_Tag::OCTET_STRING, 0);
    }
 
 /*
@@ -496,7 +496,7 @@ std::vector<uint8_t> Extended_Key_Usage::encode_inner() const
    {
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode_list(m_oids)
       .end_cons();
    return output;
@@ -526,22 +526,22 @@ void Name_Constraints::decode_inner(const std::vector<uint8_t>& in)
    {
    std::vector<GeneralSubtree> permit, exclude;
    BER_Decoder ber(in);
-   BER_Decoder ext = ber.start_cons(SEQUENCE);
+   BER_Decoder ext = ber.start_sequence();
    BER_Object per = ext.get_next_object();
 
    ext.push_back(per);
-   if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+   if(per.is_a(0, ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC))
       {
-      ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+      ext.decode_list(permit, ASN1_Tag(0), ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC);
       if(permit.empty())
          throw Encoding_Error("Empty Name Contraint list");
       }
 
    BER_Object exc = ext.get_next_object();
    ext.push_back(exc);
-   if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+   if(per.is_a(1, ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC))
       {
-      ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+      ext.decode_list(exclude, ASN1_Tag(1), ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC);
       if(exclude.empty())
          throw Encoding_Error("Empty Name Contraint list");
       }
@@ -634,14 +634,14 @@ class Policy_Information final : public ASN1_Object
 
       void encode_into(DER_Encoder& codec) const override
          {
-         codec.start_cons(SEQUENCE)
+         codec.start_sequence()
             .encode(m_oid)
             .end_cons();
          }
 
       void decode_from(BER_Decoder& codec) override
          {
-         codec.start_cons(SEQUENCE)
+         codec.start_sequence()
             .decode(m_oid)
             .discard_remaining()
             .end_cons();
@@ -665,7 +665,7 @@ std::vector<uint8_t> Certificate_Policies::encode_inner() const
 
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .encode_list(policies)
       .end_cons();
    return output;
@@ -700,14 +700,14 @@ void Certificate_Policies::validate(
 
 std::vector<uint8_t> Authority_Information_Access::encode_inner() const
    {
-   ASN1_String url(m_ocsp_responder, IA5_STRING);
+   ASN1_String url(m_ocsp_responder, ASN1_Tag::IA5_STRING);
 
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
-      .start_cons(SEQUENCE)
+      .start_sequence()
+      .start_sequence()
       .encode(OID::from_string("PKIX.OCSP"))
-      .add_object(ASN1_Tag(6), CONTEXT_SPECIFIC, url.value())
+      .add_object(ASN1_Tag(6), ASN1_Tag::CONTEXT_SPECIFIC, url.value())
       .end_cons()
       .end_cons();
    return output;
@@ -715,13 +715,13 @@ std::vector<uint8_t> Authority_Information_Access::encode_inner() const
 
 void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
    {
-   BER_Decoder ber = BER_Decoder(in).start_cons(SEQUENCE);
+   BER_Decoder ber = BER_Decoder(in).start_sequence();
 
    while(ber.more_items())
       {
       OID oid;
 
-      BER_Decoder info = ber.start_cons(SEQUENCE);
+      BER_Decoder info = ber.start_sequence();
 
       info.decode(oid);
 
@@ -729,7 +729,7 @@ void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
          {
          BER_Object name = info.get_next_object();
 
-         if(name.is_a(6, CONTEXT_SPECIFIC))
+         if(name.is_a(6, ASN1_Tag::CONTEXT_SPECIFIC))
             {
             m_ocsp_responder = ASN1::to_string(name);
             }
@@ -739,7 +739,7 @@ void Authority_Information_Access::decode_inner(const std::vector<uint8_t>& in)
          {
          BER_Object name = info.get_next_object();
 
-         if(name.is_a(6, CONTEXT_SPECIFIC))
+         if(name.is_a(6, ASN1_Tag::CONTEXT_SPECIFIC))
             {
             m_ca_issuers.push_back(ASN1::to_string(name));
             }
@@ -792,7 +792,7 @@ void CRL_Number::decode_inner(const std::vector<uint8_t>& in)
 std::vector<uint8_t> CRL_ReasonCode::encode_inner() const
    {
    std::vector<uint8_t> output;
-   DER_Encoder(output).encode(static_cast<size_t>(m_reason), ENUMERATED, UNIVERSAL);
+   DER_Encoder(output).encode(static_cast<size_t>(m_reason), ASN1_Tag::ENUMERATED, ASN1_Tag::UNIVERSAL);
    return output;
    }
 
@@ -802,7 +802,7 @@ std::vector<uint8_t> CRL_ReasonCode::encode_inner() const
 void CRL_ReasonCode::decode_inner(const std::vector<uint8_t>& in)
    {
    size_t reason_code = 0;
-   BER_Decoder(in).decode(reason_code, ENUMERATED, UNIVERSAL);
+   BER_Decoder(in).decode(reason_code, ASN1_Tag::ENUMERATED, ASN1_Tag::UNIVERSAL);
    m_reason = static_cast<CRL_Code>(reason_code);
    }
 
@@ -839,11 +839,11 @@ void CRL_Distribution_Points::Distribution_Point::encode_into(class DER_Encoder&
 
 void CRL_Distribution_Points::Distribution_Point::decode_from(class BER_Decoder& ber)
    {
-   ber.start_cons(SEQUENCE)
-      .start_cons(ASN1_Tag(0), CONTEXT_SPECIFIC)
+   ber.start_sequence()
+      .start_cons(ASN1_Tag(0), ASN1_Tag::CONTEXT_SPECIFIC)
         .decode_optional_implicit(m_point, ASN1_Tag(0),
-                                  ASN1_Tag(CONTEXT_SPECIFIC | CONSTRUCTED),
-                                  SEQUENCE, CONSTRUCTED)
+                                  ASN1_Tag::CONTEXT_SPECIFIC | ASN1_Tag::CONSTRUCTED,
+                                  ASN1_Tag::SEQUENCE, ASN1_Tag::CONSTRUCTED)
       .end_cons().end_cons();
    }
 
diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp
index 07225b2a0..4b53e249d 100644
--- a/src/lib/x509/x509_obj.cpp
+++ b/src/lib/x509/x509_obj.cpp
@@ -33,11 +33,11 @@ Pss_params decode_pss_params(const std::vector<uint8_t>& encoded_pss_params)
 
    Pss_params pss_parameter;
    BER_Decoder(encoded_pss_params)
-      .start_cons(SEQUENCE)
-         .decode_optional(pss_parameter.hash_algo, ASN1_Tag(0), PRIVATE, default_hash)
-         .decode_optional(pss_parameter.mask_gen_algo, ASN1_Tag(1), PRIVATE, default_mgf)
-         .decode_optional(pss_parameter.salt_len, ASN1_Tag(2), PRIVATE, size_t(20))
-         .decode_optional(pss_parameter.trailer_field, ASN1_Tag(3), PRIVATE, size_t(1))
+      .start_sequence()
+         .decode_optional(pss_parameter.hash_algo, ASN1_Tag(0), ASN1_Tag::PRIVATE, default_hash)
+         .decode_optional(pss_parameter.mask_gen_algo, ASN1_Tag(1), ASN1_Tag::PRIVATE, default_mgf)
+         .decode_optional(pss_parameter.salt_len, ASN1_Tag(2), ASN1_Tag::PRIVATE, size_t(20))
+         .decode_optional(pss_parameter.trailer_field, ASN1_Tag(3), ASN1_Tag::PRIVATE, size_t(1))
       .end_cons();
 
    BER_Decoder(pss_parameter.mask_gen_algo.get_parameters()).decode(pss_parameter.mask_gen_hash);
@@ -91,12 +91,12 @@ void X509_Object::load_data(DataSource& in)
 
 void X509_Object::encode_into(DER_Encoder& to) const
    {
-   to.start_cons(SEQUENCE)
-         .start_cons(SEQUENCE)
+   to.start_sequence()
+         .start_sequence()
             .raw_bytes(signed_body())
          .end_cons()
          .encode(signature_algorithm())
-         .encode(signature(), BIT_STRING)
+         .encode(signature(), ASN1_Tag::BIT_STRING)
       .end_cons();
    }
 
@@ -105,12 +105,12 @@ void X509_Object::encode_into(DER_Encoder& to) const
 */
 void X509_Object::decode_from(BER_Decoder& from)
    {
-   from.start_cons(SEQUENCE)
-         .start_cons(SEQUENCE)
+   from.start_sequence()
+         .start_sequence()
             .raw_bytes(m_tbs_bits)
          .end_cons()
          .decode(m_sig_algo)
-         .decode(m_sig, BIT_STRING)
+         .decode(m_sig, ASN1_Tag::BIT_STRING)
       .end_cons();
 
    force_decode();
@@ -309,10 +309,10 @@ std::vector<uint8_t> X509_Object::make_signed(PK_Signer* signer,
 
    std::vector<uint8_t> output;
    DER_Encoder(output)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .raw_bytes(tbs_bits)
          .encode(algo)
-         .encode(signature, BIT_STRING)
+         .encode(signature, ASN1_Tag::BIT_STRING)
       .end_cons();
 
    return output;
diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
index 8c142a6a1..a22ba484d 100644
--- a/src/lib/x509/x509cert.cpp
+++ b/src/lib/x509/x509cert.cpp
@@ -114,18 +114,18 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
    BER_Object v3_exts_data;
 
    BER_Decoder(obj.signed_body())
-      .decode_optional(data->m_version, ASN1_Tag(0), ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC))
+      .decode_optional(data->m_version, ASN1_Tag(0), ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC)
       .decode(serial_bn)
       .decode(data->m_sig_algo_inner)
       .decode(data->m_issuer_dn)
-      .start_cons(SEQUENCE)
+      .start_sequence()
          .decode(data->m_not_before)
          .decode(data->m_not_after)
       .end_cons()
       .decode(data->m_subject_dn)
       .get_next(public_key)
-      .decode_optional_string(data->m_v2_issuer_key_id, BIT_STRING, 1)
-      .decode_optional_string(data->m_v2_subject_key_id, BIT_STRING, 2)
+      .decode_optional_string(data->m_v2_issuer_key_id, ASN1_Tag::BIT_STRING, 1)
+      .decode_optional_string(data->m_v2_subject_key_id, ASN1_Tag::BIT_STRING, 2)
       .get_next(v3_exts_data)
       .verify_end("TBSCertificate has extra data after extensions block");
 
@@ -134,7 +134,7 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
    if(obj.signature_algorithm() != data->m_sig_algo_inner)
       throw Decoding_Error("X.509 Certificate had differing algorithm identifers in inner and outer ID fields");
 
-   public_key.assert_is_a(SEQUENCE, CONSTRUCTED, "X.509 certificate public key");
+   public_key.assert_is_a(ASN1_Tag::SEQUENCE, ASN1_Tag::CONSTRUCTED, "X.509 certificate public key");
 
    // crude method to save the serial's sign; will get lost during decoding, otherwise
    data->m_serial_negative = serial_bn.is_negative();
@@ -196,9 +196,9 @@ std::unique_ptr<X509_Certificate_Data> parse_x509_cert_body(const X509_Object& o
 
    BER_Decoder(data->m_subject_public_key_bits)
       .decode(data->m_subject_public_key_algid)
-      .decode(data->m_subject_public_key_bitstring, BIT_STRING);
+      .decode(data->m_subject_public_key_bitstring, ASN1_Tag::BIT_STRING);
 
-   if(v3_exts_data.is_a(3, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+   if(v3_exts_data.is_a(3, ASN1_Tag::CONSTRUCTED | ASN1_Tag::CONTEXT_SPECIFIC))
       {
       // Path validation will reject a v1/v2 cert with v3 extensions
       BER_Decoder(v3_exts_data).decode(data->m_v3_extensions).verify_end();
diff --git a/src/lib/x509/x509self.cpp b/src/lib/x509/x509self.cpp
index f75827fd8..e2282de9e 100644
--- a/src/lib/x509/x509self.cpp
+++ b/src/lib/x509/x509self.cpp
@@ -36,7 +36,7 @@ void load_info(const X509_Cert_Options& opts, X509_DN& subject_dn,
    subject_dn.add_attribute("X520.SerialNumber", opts.serial_number);
    subject_alt = AlternativeName(opts.email, opts.uri, opts.dns, opts.ip);
    subject_alt.add_othername(OID::from_string("PKIX.XMPPAddr"),
-                             opts.xmpp, UTF8_STRING);
+                             opts.xmpp, ASN1_Tag::UTF8_STRING);
 
    for(auto dns : opts.more_dns)
       subject_alt.add_attribute("DNS", dns);


Test patch:
diff --git a/src/tests/test_asn1.cpp b/src/tests/test_asn1.cpp
index ab276cc8a..69ae643b2 100644
--- a/src/tests/test_asn1.cpp
+++ b/src/tests/test_asn1.cpp
@@ -310,7 +310,7 @@ class ASN1_Time_Parsing_Tests final : public Text_Based_Test
             }
 
          const Botan::ASN1_Tag tag =
-            (tag_str == "UTC" || tag_str == "UTC.invalid") ? Botan::UTC_TIME : Botan::GENERALIZED_TIME;
+            (tag_str == "UTC" || tag_str == "UTC.invalid") ? Botan::ASN1_Tag::UTC_TIME : Botan::ASN1_Tag::GENERALIZED_TIME;
 
          const bool valid = tag_str.find(".invalid") == std::string::npos;
 
diff --git a/src/tests/test_hash_id.cpp b/src/tests/test_hash_id.cpp
index 269d57e79..9cc75d14e 100644
--- a/src/tests/test_hash_id.cpp
+++ b/src/tests/test_hash_id.cpp
@@ -56,7 +56,7 @@ class PKCS_HashID_Test final : public Test
 
                std::vector<uint8_t> bits;
                Botan::DER_Encoder der(bits);
-               der.start_cons(Botan::SEQUENCE).encode(alg).encode(dummy_hash, Botan::OCTET_STRING).end_cons();
+               der.start_sequence().encode(alg).encode(dummy_hash, Botan::ASN1_Tag::OCTET_STRING).end_cons();
 
                result.test_eq("Dummy hash is expected size", bits.size() - pkcs_id.size(), dummy_hash.size());
 
diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp
index ce2d43639..db02d29a5 100644
--- a/src/tests/test_pkcs11_high_level.cpp
+++ b/src/tests/test_pkcs11_high_level.cpp
@@ -914,7 +914,7 @@ BOTAN_REGISTER_TEST("pkcs11", "pkcs11-rsa", PKCS11_RSA_Tests);
 std::vector<uint8_t> encode_ec_point_in_octet_str(const Botan::PointGFp& point)
    {
    std::vector<uint8_t> enc;
-   DER_Encoder(enc).encode(point.encode(PointGFp::UNCOMPRESSED), OCTET_STRING);
+   DER_Encoder(enc).encode(point.encode(PointGFp::UNCOMPRESSED), ASN1_Tag::OCTET_STRING);
    return enc;
    }
 #endif
diff --git a/src/tests/unit_x509.cpp b/src/tests/unit_x509.cpp
index 605297cd3..13bebc7d3 100644
--- a/src/tests/unit_x509.cpp
+++ b/src/tests/unit_x509.cpp
@@ -1356,14 +1356,14 @@ class String_Extension final : public Botan::Certificate_Extension
       std::vector<uint8_t> encode_inner() const override
          {
          std::vector<uint8_t> bits;
-         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::UTF8_STRING));
+         Botan::DER_Encoder(bits).encode(Botan::ASN1_String(m_contents, Botan::ASN1_Tag::UTF8_STRING));
          return bits;
          }
 
       void decode_inner(const std::vector<uint8_t>& in) override
          {
          Botan::ASN1_String str;
-         Botan::BER_Decoder(in).decode(str, Botan::UTF8_STRING).verify_end();
+         Botan::BER_Decoder(in).decode(str, Botan::ASN1_Tag::UTF8_STRING).verify_end();
          m_contents = str.value();
          }
 
@@ -1391,8 +1391,8 @@ Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
 
    const Botan::OID attr1(Botan::OID("1.3.6.1.4.1.25258.9.1.1"));
    const Botan::OID attr2(Botan::OID("1.3.6.1.4.1.25258.9.1.2"));
-   const Botan::ASN1_String val1("Custom Attr 1", Botan::PRINTABLE_STRING);
-   const Botan::ASN1_String val2("12345", Botan::UTF8_STRING);
+   const Botan::ASN1_String val1("Custom Attr 1", Botan::ASN1_Tag::PRINTABLE_STRING);
+   const Botan::ASN1_String val2("12345", Botan::ASN1_Tag::UTF8_STRING);
 
    subject_dn.add_attribute(attr1, val1);
    subject_dn.add_attribute(attr2, val2);
@@ -1418,8 +1418,8 @@ Test::Result test_custom_dn_attr(const Botan::Private_Key& ca_key,
    result.confirm("Attr1 tag matches encoded", req_val1.tagging() == val1.tagging());
    result.confirm("Attr2 tag matches encoded", req_val2.tagging() == val2.tagging());
 
-   Botan::X509_Time not_before("100301123001Z", Botan::UTC_TIME);
-   Botan::X509_Time not_after("300301123001Z", Botan::UTC_TIME);
+   Botan::X509_Time not_before("100301123001Z", Botan::ASN1_Tag::UTC_TIME);
+   Botan::X509_Time not_after("300301123001Z", Botan::ASN1_Tag::UTC_TIME);
 
    auto cert = ca.sign_request(req, Test::rng(), not_before, not_after);