Add CBB_add_asn1_element convenience function

This is common enough that we probably should have one available. Saves
declaring a variable, calling flush, etc.

Change-Id: Ib21627e9e4caea01c729ad0dbb6266fdd30bb533
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/78788
Commit-Queue: Adam Langley <agl@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index f2604ac..36ef5d6 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -3002,9 +3002,8 @@
   // Test only the first field present.
   bssl::ScopedCBB cbb;
   ASSERT_TRUE(CBB_init(cbb.get(), 64));
-  CBB seq;
-  ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
-  ASSERT_TRUE(CBB_add_bytes(&seq, inp.data(), inp.size()));
+  ASSERT_TRUE(CBB_add_asn1_element(cbb.get(), CBS_ASN1_SEQUENCE, inp.data(),
+                                   inp.size()));
   ASSERT_TRUE(CBB_flush(cbb.get()));
   const uint8_t *ptr = CBB_data(cbb.get());
   obj.reset(d2i_embed(nullptr, &ptr, CBB_len(cbb.get())));
@@ -3020,6 +3019,7 @@
   // Test all fields present.
   cbb.Reset();
   ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  CBB seq;
   ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
   ASSERT_TRUE(CBB_add_bytes(&seq, inp.data(), inp.size()));
   CBB child;
diff --git a/crypto/bytestring/cbb.cc b/crypto/bytestring/cbb.cc
index 316ac4a..b847331 100644
--- a/crypto/bytestring/cbb.cc
+++ b/crypto/bytestring/cbb.cc
@@ -481,6 +481,19 @@
   cbb->child = NULL;
 }
 
+int CBB_add_asn1_element(CBB *cbb, CBS_ASN1_TAG tag, const uint8_t *data,
+                         size_t data_len) {
+  CBB child;
+  if (!CBB_add_asn1(cbb, &child, tag) ||
+      !CBB_add_bytes(&child, data, data_len) ||  //
+      !CBB_flush(cbb)) {
+    cbb_on_error(cbb);
+    return 0;
+  }
+
+  return 1;
+}
+
 int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {
   return CBB_add_asn1_uint64_with_tag(cbb, value, CBS_ASN1_INTEGER);
 }
@@ -557,14 +570,7 @@
 }
 
 int CBB_add_asn1_octet_string(CBB *cbb, const uint8_t *data, size_t data_len) {
-  CBB child;
-  if (!CBB_add_asn1(cbb, &child, CBS_ASN1_OCTETSTRING) ||
-      !CBB_add_bytes(&child, data, data_len) || !CBB_flush(cbb)) {
-    cbb_on_error(cbb);
-    return 0;
-  }
-
-  return 1;
+  return CBB_add_asn1_element(cbb, CBS_ASN1_OCTETSTRING, data, data_len);
 }
 
 int CBB_add_asn1_bool(CBB *cbb, int value) {
diff --git a/crypto/ec/ec_asn1.cc b/crypto/ec/ec_asn1.cc
index c5ced25..ce81580 100644
--- a/crypto/ec/ec_asn1.cc
+++ b/crypto/ec/ec_asn1.cc
@@ -320,10 +320,7 @@
     return 0;
   }
 
-  CBB child;
-  return CBB_add_asn1(cbb, &child, CBS_ASN1_OBJECT) &&
-         CBB_add_bytes(&child, group->oid, group->oid_len) &&  //
-         CBB_flush(cbb);
+  return CBB_add_asn1_element(cbb, CBS_ASN1_OBJECT, group->oid, group->oid_len);
 }
 
 EC_GROUP *EC_KEY_parse_parameters(CBS *cbs) {
diff --git a/crypto/evp/p_dsa_asn1.cc b/crypto/evp/p_dsa_asn1.cc
index aab3f59..8503d4d 100644
--- a/crypto/evp/p_dsa_asn1.cc
+++ b/crypto/evp/p_dsa_asn1.cc
@@ -62,11 +62,11 @@
       dsa->p != nullptr && dsa->q != nullptr && dsa->g != nullptr;
 
   // See RFC 5480, section 2.
-  CBB spki, algorithm, oid, key_bitstring;
+  CBB spki, algorithm, key_bitstring;
   if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, dsa_asn1_meth.oid, dsa_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, dsa_asn1_meth.oid,
+                            dsa_asn1_meth.oid_len) ||
       (has_params && !DSA_marshal_parameters(&algorithm, dsa)) ||
       !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
       !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
@@ -126,12 +126,12 @@
   }
 
   // See PKCS#11, v2.40, section 2.5.
-  CBB pkcs8, algorithm, oid, private_key;
+  CBB pkcs8, algorithm, private_key;
   if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
       !CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, dsa_asn1_meth.oid, dsa_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, dsa_asn1_meth.oid,
+                            dsa_asn1_meth.oid_len) ||
       !DSA_marshal_parameters(&algorithm, dsa) ||
       !CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
       !BN_marshal_asn1(&private_key, dsa->priv_key) || !CBB_flush(out)) {
diff --git a/crypto/evp/p_ec_asn1.cc b/crypto/evp/p_ec_asn1.cc
index bcf2525..d89c6eb 100644
--- a/crypto/evp/p_ec_asn1.cc
+++ b/crypto/evp/p_ec_asn1.cc
@@ -30,11 +30,11 @@
   const EC_POINT *public_key = EC_KEY_get0_public_key(ec_key);
 
   // See RFC 5480, section 2.
-  CBB spki, algorithm, oid, key_bitstring;
+  CBB spki, algorithm, key_bitstring;
   if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, ec_asn1_meth.oid, ec_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, ec_asn1_meth.oid,
+                            ec_asn1_meth.oid_len) ||
       !EC_KEY_marshal_curve_name(&algorithm, group) ||
       !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
       !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
@@ -114,12 +114,12 @@
   unsigned enc_flags = EC_KEY_get_enc_flags(ec_key) | EC_PKEY_NO_PARAMETERS;
 
   // See RFC 5915.
-  CBB pkcs8, algorithm, oid, private_key;
+  CBB pkcs8, algorithm, private_key;
   if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
       !CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, ec_asn1_meth.oid, ec_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, ec_asn1_meth.oid,
+                            ec_asn1_meth.oid_len) ||
       !EC_KEY_marshal_curve_name(&algorithm, EC_KEY_get0_group(ec_key)) ||
       !CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
       !EC_KEY_marshal_private_key(&private_key, ec_key, enc_flags) ||
diff --git a/crypto/evp/p_ed25519_asn1.cc b/crypto/evp/p_ed25519_asn1.cc
index 45947d8..8053422 100644
--- a/crypto/evp/p_ed25519_asn1.cc
+++ b/crypto/evp/p_ed25519_asn1.cc
@@ -129,11 +129,11 @@
   const ED25519_KEY *key = reinterpret_cast<const ED25519_KEY *>(pkey->pkey);
 
   // See RFC 8410, section 4.
-  CBB spki, algorithm, oid, key_bitstring;
+  CBB spki, algorithm, key_bitstring;
   if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, ed25519_asn1_meth.oid, ed25519_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, ed25519_asn1_meth.oid,
+                            ed25519_asn1_meth.oid_len) ||
       !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
       !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
       !CBB_add_bytes(&key_bitstring, key->key + ED25519_PUBLIC_KEY_OFFSET,
@@ -176,12 +176,12 @@
   }
 
   // See RFC 8410, section 7.
-  CBB pkcs8, algorithm, oid, private_key, inner;
+  CBB pkcs8, algorithm, private_key, inner;
   if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
       !CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, ed25519_asn1_meth.oid, ed25519_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, ed25519_asn1_meth.oid,
+                            ed25519_asn1_meth.oid_len) ||
       !CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
       !CBB_add_asn1(&private_key, &inner, CBS_ASN1_OCTETSTRING) ||
       // The PKCS#8 encoding stores only the 32-byte seed which is the first 32
diff --git a/crypto/evp/p_rsa_asn1.cc b/crypto/evp/p_rsa_asn1.cc
index cdcb6b4..6c6bfc5 100644
--- a/crypto/evp/p_rsa_asn1.cc
+++ b/crypto/evp/p_rsa_asn1.cc
@@ -28,11 +28,11 @@
 static int rsa_pub_encode(CBB *out, const EVP_PKEY *key) {
   // See RFC 3279, section 2.3.1.
   const RSA *rsa = reinterpret_cast<const RSA *>(key->pkey);
-  CBB spki, algorithm, oid, null, key_bitstring;
+  CBB spki, algorithm, null, key_bitstring;
   if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, rsa_asn1_meth.oid, rsa_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, rsa_asn1_meth.oid,
+                            rsa_asn1_meth.oid_len) ||
       !CBB_add_asn1(&algorithm, &null, CBS_ASN1_NULL) ||
       !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
       !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
@@ -76,12 +76,12 @@
 
 static int rsa_priv_encode(CBB *out, const EVP_PKEY *key) {
   const RSA *rsa = reinterpret_cast<const RSA *>(key->pkey);
-  CBB pkcs8, algorithm, oid, null, private_key;
+  CBB pkcs8, algorithm, null, private_key;
   if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
       !CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, rsa_asn1_meth.oid, rsa_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, rsa_asn1_meth.oid,
+                            rsa_asn1_meth.oid_len) ||
       !CBB_add_asn1(&algorithm, &null, CBS_ASN1_NULL) ||
       !CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
       !RSA_marshal_private_key(&private_key, rsa) ||  //
diff --git a/crypto/evp/p_x25519_asn1.cc b/crypto/evp/p_x25519_asn1.cc
index 351b788..c31dcc5 100644
--- a/crypto/evp/p_x25519_asn1.cc
+++ b/crypto/evp/p_x25519_asn1.cc
@@ -143,11 +143,11 @@
   const X25519_KEY *key = reinterpret_cast<X25519_KEY *>(pkey->pkey);
 
   // See RFC 8410, section 4.
-  CBB spki, algorithm, oid, key_bitstring;
+  CBB spki, algorithm, key_bitstring;
   if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, x25519_asn1_meth.oid, x25519_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, x25519_asn1_meth.oid,
+                            x25519_asn1_meth.oid_len) ||
       !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
       !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
       !CBB_add_bytes(&key_bitstring, key->pub, 32) ||  //
@@ -188,12 +188,12 @@
   }
 
   // See RFC 8410, section 7.
-  CBB pkcs8, algorithm, oid, private_key, inner;
+  CBB pkcs8, algorithm, private_key, inner;
   if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
       !CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, x25519_asn1_meth.oid, x25519_asn1_meth.oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, x25519_asn1_meth.oid,
+                            x25519_asn1_meth.oid_len) ||
       !CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
       !CBB_add_asn1(&private_key, &inner, CBS_ASN1_OCTETSTRING) ||
       // The PKCS#8 encoding stores only the 32-byte seed which is the first 32
diff --git a/crypto/obj/obj.cc b/crypto/obj/obj.cc
index ffc12ff..9fa26bb 100644
--- a/crypto/obj/obj.cc
+++ b/crypto/obj/obj.cc
@@ -278,14 +278,8 @@
 
 OPENSSL_EXPORT int OBJ_nid2cbb(CBB *out, int nid) {
   const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
-  CBB oid;
-
-  if (obj == NULL || !CBB_add_asn1(out, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, obj->data, obj->length) || !CBB_flush(out)) {
-    return 0;
-  }
-
-  return 1;
+  return obj != NULL &&
+         CBB_add_asn1_element(out, CBS_ASN1_OBJECT, obj->data, obj->length);
 }
 
 const ASN1_OBJECT *OBJ_get_undef(void) {
diff --git a/crypto/pkcs7/pkcs7.cc b/crypto/pkcs7/pkcs7.cc
index 2a5a5a7..aa92cc1 100644
--- a/crypto/pkcs7/pkcs7.cc
+++ b/crypto/pkcs7/pkcs7.cc
@@ -167,13 +167,12 @@
                           int (*cert_crl_cb)(CBB *out, void *arg),
                           int (*signer_infos_cb)(CBB *out, void *arg),
                           void *arg) {
-  CBB outer_seq, oid, wrapped_seq, seq, digest_algos_set, content_info,
-      signer_infos;
+  CBB outer_seq, wrapped_seq, seq, digest_algos_set, content_info, signer_infos;
 
   // See https://tools.ietf.org/html/rfc2315#section-7
   if (!CBB_add_asn1(out, &outer_seq, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&outer_seq, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, kPKCS7SignedData, sizeof(kPKCS7SignedData)) ||
+      !CBB_add_asn1_element(&outer_seq, CBS_ASN1_OBJECT, kPKCS7SignedData,
+                            sizeof(kPKCS7SignedData)) ||
       !CBB_add_asn1(&outer_seq, &wrapped_seq,
                     CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
       // See https://tools.ietf.org/html/rfc2315#section-9.1
@@ -183,8 +182,8 @@
       (digest_algos_cb != NULL && !digest_algos_cb(&digest_algos_set, arg)) ||
       !CBB_flush_asn1_set_of(&digest_algos_set) ||
       !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
+      !CBB_add_asn1_element(&content_info, CBS_ASN1_OBJECT, kPKCS7Data,
+                            sizeof(kPKCS7Data)) ||
       (cert_crl_cb != NULL && !cert_crl_cb(&seq, arg)) ||
       !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
       (signer_infos_cb != NULL && !signer_infos_cb(&signer_infos, arg)) ||
diff --git a/crypto/pkcs7/pkcs7_x509.cc b/crypto/pkcs7/pkcs7_x509.cc
index d762da1..8933f61 100644
--- a/crypto/pkcs7/pkcs7_x509.cc
+++ b/crypto/pkcs7/pkcs7_x509.cc
@@ -422,9 +422,9 @@
       return 0;
     }
     // subjectKeyIdentifier is implicitly-tagged.
-    if (!CBB_add_asn1(&seq, &child, CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
-        !CBB_add_bytes(&child, ASN1_STRING_get0_data(skid),
-                       ASN1_STRING_length(skid))) {
+    if (!CBB_add_asn1_element(&seq, CBS_ASN1_CONTEXT_SPECIFIC | 0,
+                              ASN1_STRING_get0_data(skid),
+                              ASN1_STRING_length(skid))) {
       return 0;
     }
   } else {
diff --git a/crypto/pkcs8/p5_pbev2.cc b/crypto/pkcs8/p5_pbev2.cc
index 7117763..bac897e 100644
--- a/crypto/pkcs8/p5_pbev2.cc
+++ b/crypto/pkcs8/p5_pbev2.cc
@@ -90,10 +90,8 @@
 static int add_cipher_oid(CBB *out, int nid) {
   for (const auto &cipher : kCipherOIDs) {
     if (cipher.nid == nid) {
-      CBB child;
-      return CBB_add_asn1(out, &child, CBS_ASN1_OBJECT) &&
-             CBB_add_bytes(&child, cipher.oid, cipher.oid_len) &&
-             CBB_flush(out);
+      return CBB_add_asn1_element(out, CBS_ASN1_OBJECT, cipher.oid,
+                                  cipher.oid_len);
     }
   }
 
@@ -145,18 +143,15 @@
   }
 
   // See RFC 2898, appendix A.
-  CBB algorithm, oid, param, kdf, kdf_oid, kdf_param, salt_cbb, cipher_cbb,
-      iv_cbb;
+  CBB algorithm, param, kdf, kdf_param, cipher_cbb;
   if (!CBB_add_asn1(out, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, kPBES2, sizeof(kPBES2)) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, kPBES2,
+                            sizeof(kPBES2)) ||
       !CBB_add_asn1(&algorithm, &param, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&param, &kdf, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&kdf, &kdf_oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&kdf_oid, kPBKDF2, sizeof(kPBKDF2)) ||
+      !CBB_add_asn1_element(&kdf, CBS_ASN1_OBJECT, kPBKDF2, sizeof(kPBKDF2)) ||
       !CBB_add_asn1(&kdf, &kdf_param, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&kdf_param, &salt_cbb, CBS_ASN1_OCTETSTRING) ||
-      !CBB_add_bytes(&salt_cbb, salt, salt_len) ||
+      !CBB_add_asn1_octet_string(&kdf_param, salt, salt_len) ||
       !CBB_add_asn1_uint64(&kdf_param, iterations) ||
       // Specify a key length for RC2.
       (cipher_nid == NID_rc2_cbc &&
@@ -167,8 +162,8 @@
       !add_cipher_oid(&cipher_cbb, cipher_nid) ||
       // RFC 2898 says RC2-CBC and RC5-CBC-Pad use a SEQUENCE with version and
       // IV, but OpenSSL always uses an OCTET STRING IV, so we do the same.
-      !CBB_add_asn1(&cipher_cbb, &iv_cbb, CBS_ASN1_OCTETSTRING) ||
-      !CBB_add_bytes(&iv_cbb, iv, EVP_CIPHER_iv_length(cipher)) ||
+      !CBB_add_asn1_octet_string(&cipher_cbb, iv,
+                                 EVP_CIPHER_iv_length(cipher)) ||
       !CBB_flush(out)) {
     return 0;
   }
diff --git a/crypto/pkcs8/pkcs8.cc b/crypto/pkcs8/pkcs8.cc
index a6937c1..4b5609d 100644
--- a/crypto/pkcs8/pkcs8.cc
+++ b/crypto/pkcs8/pkcs8.cc
@@ -302,13 +302,12 @@
   }
 
   // See RFC 2898, appendix A.3.
-  CBB algorithm, oid, param, salt_cbb;
+  CBB algorithm, param;
   if (!CBB_add_asn1(out, &algorithm, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&oid, suite->oid, suite->oid_len) ||
+      !CBB_add_asn1_element(&algorithm, CBS_ASN1_OBJECT, suite->oid,
+                            suite->oid_len) ||
       !CBB_add_asn1(&algorithm, &param, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&param, &salt_cbb, CBS_ASN1_OCTETSTRING) ||
-      !CBB_add_bytes(&salt_cbb, salt, salt_len) ||
+      !CBB_add_asn1_octet_string(&param, salt, salt_len) ||
       !CBB_add_asn1_uint64(&param, iterations) || !CBB_flush(out)) {
     return 0;
   }
diff --git a/crypto/pkcs8/pkcs8_x509.cc b/crypto/pkcs8/pkcs8_x509.cc
index ede84f7..c5abc75 100644
--- a/crypto/pkcs8/pkcs8_x509.cc
+++ b/crypto/pkcs8/pkcs8_x509.cc
@@ -904,15 +904,15 @@
     return 1;  // Omit the OPTIONAL SET.
   }
   // See https://tools.ietf.org/html/rfc7292#section-4.2.
-  CBB attrs, attr, oid, values, value;
+  CBB attrs, attr, values, value;
   if (!CBB_add_asn1(bag, &attrs, CBS_ASN1_SET)) {
     return 0;
   }
   if (name_len != 0) {
     // See https://tools.ietf.org/html/rfc2985, section 5.5.1.
     if (!CBB_add_asn1(&attrs, &attr, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1(&attr, &oid, CBS_ASN1_OBJECT) ||
-        !CBB_add_bytes(&oid, kFriendlyName, sizeof(kFriendlyName)) ||
+        !CBB_add_asn1_element(&attr, CBS_ASN1_OBJECT, kFriendlyName,
+                              sizeof(kFriendlyName)) ||
         !CBB_add_asn1(&attr, &values, CBS_ASN1_SET) ||
         !CBB_add_asn1(&values, &value, CBS_ASN1_BMPSTRING)) {
       return 0;
@@ -931,11 +931,10 @@
   if (key_id_len != 0) {
     // See https://tools.ietf.org/html/rfc2985, section 5.5.2.
     if (!CBB_add_asn1(&attrs, &attr, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1(&attr, &oid, CBS_ASN1_OBJECT) ||
-        !CBB_add_bytes(&oid, kLocalKeyID, sizeof(kLocalKeyID)) ||
+        !CBB_add_asn1_element(&attr, CBS_ASN1_OBJECT, kLocalKeyID,
+                              sizeof(kLocalKeyID)) ||
         !CBB_add_asn1(&attr, &values, CBS_ASN1_SET) ||
-        !CBB_add_asn1(&values, &value, CBS_ASN1_OCTETSTRING) ||
-        !CBB_add_bytes(&value, key_id, key_id_len)) {
+        !CBB_add_asn1_octet_string(&values, key_id, key_id_len)) {
       return 0;
     }
   }
@@ -944,17 +943,17 @@
 
 static int add_cert_bag(CBB *cbb, X509 *cert, const char *name,
                         const uint8_t *key_id, size_t key_id_len) {
-  CBB bag, bag_oid, bag_contents, cert_bag, cert_type, wrapped_cert, cert_value;
+  CBB bag, bag_contents, cert_bag, wrapped_cert, cert_value;
   if (  // See https://tools.ietf.org/html/rfc7292#section-4.2.
       !CBB_add_asn1(cbb, &bag, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&bag, &bag_oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&bag_oid, kCertBag, sizeof(kCertBag)) ||
+      !CBB_add_asn1_element(&bag, CBS_ASN1_OBJECT, kCertBag,
+                            sizeof(kCertBag)) ||
       !CBB_add_asn1(&bag, &bag_contents,
                     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
       // See https://tools.ietf.org/html/rfc7292#section-4.2.3.
       !CBB_add_asn1(&bag_contents, &cert_bag, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&cert_bag, &cert_type, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&cert_type, kX509Certificate, sizeof(kX509Certificate)) ||
+      !CBB_add_asn1_element(&cert_bag, CBS_ASN1_OBJECT, kX509Certificate,
+                            sizeof(kX509Certificate)) ||
       !CBB_add_asn1(&cert_bag, &wrapped_cert,
                     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
       !CBB_add_asn1(&wrapped_cert, &cert_value, CBS_ASN1_OCTETSTRING)) {
@@ -1019,12 +1018,12 @@
   }
 
   bssl::ScopedEVP_CIPHER_CTX ctx;
-  CBB content_info, type, wrapper, encrypted_data, encrypted_content_info,
-      inner_type, encrypted_content;
+  CBB content_info, wrapper, encrypted_data, encrypted_content_info,
+      encrypted_content;
   if (  // Add the ContentInfo wrapping.
       !CBB_add_asn1(out, &content_info, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&content_info, &type, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&type, kPKCS7EncryptedData, sizeof(kPKCS7EncryptedData)) ||
+      !CBB_add_asn1_element(&content_info, CBS_ASN1_OBJECT, kPKCS7EncryptedData,
+                            sizeof(kPKCS7EncryptedData)) ||
       !CBB_add_asn1(&content_info, &wrapper,
                     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
       // See https://tools.ietf.org/html/rfc2315#section-13.
@@ -1033,8 +1032,8 @@
       // See https://tools.ietf.org/html/rfc2315#section-10.1.
       !CBB_add_asn1(&encrypted_data, &encrypted_content_info,
                     CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&encrypted_content_info, &inner_type, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&inner_type, kPKCS7Data, sizeof(kPKCS7Data)) ||
+      !CBB_add_asn1_element(&encrypted_content_info, CBS_ASN1_OBJECT,
+                            kPKCS7Data, sizeof(kPKCS7Data)) ||
       // Set up encryption and fill in contentEncryptionAlgorithm.
       !pkcs12_pbe_encrypt_init(&encrypted_content_info, ctx.get(), pbe_nid,
                                pbe_cipher, iterations, password, password_len,
@@ -1143,15 +1142,14 @@
 
   // See https://tools.ietf.org/html/rfc7292#section-4.
   PKCS12 *ret = NULL;
-  CBB cbb, pfx, auth_safe, auth_safe_oid, auth_safe_wrapper, auth_safe_data,
-      content_infos;
+  CBB cbb, pfx, auth_safe, auth_safe_wrapper, auth_safe_data, content_infos;
   uint8_t mac_key[EVP_MAX_MD_SIZE];
   if (!CBB_init(&cbb, 0) || !CBB_add_asn1(&cbb, &pfx, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&pfx, 3) ||
       // auth_safe is a data ContentInfo.
       !CBB_add_asn1(&pfx, &auth_safe, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_asn1(&auth_safe, &auth_safe_oid, CBS_ASN1_OBJECT) ||
-      !CBB_add_bytes(&auth_safe_oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
+      !CBB_add_asn1_element(&auth_safe, CBS_ASN1_OBJECT, kPKCS7Data,
+                            sizeof(kPKCS7Data)) ||
       !CBB_add_asn1(&auth_safe, &auth_safe_wrapper,
                     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
       !CBB_add_asn1(&auth_safe_wrapper, &auth_safe_data,
@@ -1171,10 +1169,10 @@
       // OpenSSL does not do this. We keep them separate for consistency. (Keys,
       // even when encrypted, are always placed in unencrypted ContentInfos.
       // PKCS#12 defines bag-level encryption for keys.)
-      CBB content_info, oid, wrapper, data;
+      CBB content_info, wrapper, data;
       if (!CBB_add_asn1(&content_infos, &content_info, CBS_ASN1_SEQUENCE) ||
-          !CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
-          !CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
+          !CBB_add_asn1_element(&content_info, CBS_ASN1_OBJECT, kPKCS7Data,
+                                sizeof(kPKCS7Data)) ||
           !CBB_add_asn1(&content_info, &wrapper,
                         CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
           !CBB_add_asn1(&wrapper, &data, CBS_ASN1_OCTETSTRING) ||
@@ -1211,23 +1209,22 @@
   // inside an encrypted ContentInfo, but OpenSSL does not do this and some
   // PKCS#12 consumers do not support KeyBags.)
   if (pkey != NULL) {
-    CBB content_info, oid, wrapper, data, safe_contents, bag, bag_oid,
-        bag_contents;
+    CBB content_info, wrapper, data, safe_contents, bag, bag_contents;
     if (  // Add another data ContentInfo.
         !CBB_add_asn1(&content_infos, &content_info, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
-        !CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
+        !CBB_add_asn1_element(&content_info, CBS_ASN1_OBJECT, kPKCS7Data,
+                              sizeof(kPKCS7Data)) ||
         !CBB_add_asn1(&content_info, &wrapper,
                       CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
         !CBB_add_asn1(&wrapper, &data, CBS_ASN1_OCTETSTRING) ||
         !CBB_add_asn1(&data, &safe_contents, CBS_ASN1_SEQUENCE) ||
         // Add a SafeBag containing a PKCS8ShroudedKeyBag.
-        !CBB_add_asn1(&safe_contents, &bag, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1(&bag, &bag_oid, CBS_ASN1_OBJECT)) {
+        !CBB_add_asn1(&safe_contents, &bag, CBS_ASN1_SEQUENCE)) {
       goto err;
     }
     if (key_nid < 0) {
-      if (!CBB_add_bytes(&bag_oid, kKeyBag, sizeof(kKeyBag)) ||
+      if (!CBB_add_asn1_element(&bag, CBS_ASN1_OBJECT, kKeyBag,
+                                sizeof(kKeyBag)) ||
           !CBB_add_asn1(&bag, &bag_contents,
                         CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
           !EVP_marshal_private_key(&bag_contents, pkey)) {
@@ -1241,8 +1238,8 @@
       if (cipher != nullptr) {
         key_nid = -1;
       }
-      if (!CBB_add_bytes(&bag_oid, kPKCS8ShroudedKeyBag,
-                         sizeof(kPKCS8ShroudedKeyBag)) ||
+      if (!CBB_add_asn1_element(&bag, CBS_ASN1_OBJECT, kPKCS8ShroudedKeyBag,
+                                sizeof(kPKCS8ShroudedKeyBag)) ||
           !CBB_add_asn1(&bag, &bag_contents,
                         CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
           !PKCS8_marshal_encrypted_private_key(
@@ -1279,7 +1276,7 @@
       goto err;
     }
 
-    CBB mac_data, digest_info, mac_cbb, mac_salt_cbb;
+    CBB mac_data, digest_info;
     if (!CBB_add_asn1(&pfx, &mac_data, CBS_ASN1_SEQUENCE) ||
         !CBB_add_asn1(&mac_data, &digest_info, CBS_ASN1_SEQUENCE) ||
         // OpenSSL and NSS always include a NULL parameter with the digest
@@ -1293,10 +1290,8 @@
         //
         // We match OpenSSL, NSS, and RSASSA-PKCS1-v1_5 in including the NULL.
         !EVP_marshal_digest_algorithm(&digest_info, mac_md) ||
-        !CBB_add_asn1(&digest_info, &mac_cbb, CBS_ASN1_OCTETSTRING) ||
-        !CBB_add_bytes(&mac_cbb, mac, mac_len) ||
-        !CBB_add_asn1(&mac_data, &mac_salt_cbb, CBS_ASN1_OCTETSTRING) ||
-        !CBB_add_bytes(&mac_salt_cbb, mac_salt, sizeof(mac_salt)) ||
+        !CBB_add_asn1_octet_string(&digest_info, mac, mac_len) ||
+        !CBB_add_asn1_octet_string(&mac_data, mac_salt, sizeof(mac_salt)) ||
         // The iteration count has a DEFAULT of 1, but RFC 7292 says "The
         // default is for historical reasons and its use is deprecated." Thus we
         // explicitly encode the iteration count, though it is not valid DER.
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index b7e9fbf..aa96a28 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <functional>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -7151,20 +7152,16 @@
   static const char kOIDText[] = "1.2.840.113554.4.1.72585.0";
 
   auto encode_single_attribute_name =
-      [](CBS_ASN1_TAG tag,
-         const std::string &contents) -> std::vector<uint8_t> {
+      [](CBS_ASN1_TAG tag, std::string_view contents) -> std::vector<uint8_t> {
+    auto bytes = bssl::StringAsBytes(contents);
     bssl::ScopedCBB cbb;
-    CBB seq, rdn, attr, attr_type, attr_value;
+    CBB seq, rdn, attr;
     if (!CBB_init(cbb.get(), 128) ||
         !CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE) ||
         !CBB_add_asn1(&seq, &rdn, CBS_ASN1_SET) ||
         !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1(&attr, &attr_type, CBS_ASN1_OBJECT) ||
-        !CBB_add_bytes(&attr_type, kOID, sizeof(kOID)) ||
-        !CBB_add_asn1(&attr, &attr_value, tag) ||
-        !CBB_add_bytes(&attr_value,
-                       reinterpret_cast<const uint8_t *>(contents.data()),
-                       contents.size()) ||
+        !CBB_add_asn1_element(&attr, CBS_ASN1_OBJECT, kOID, sizeof(kOID)) ||
+        !CBB_add_asn1_element(&attr, tag, bytes.data(), bytes.size()) ||
         !CBB_flush(cbb.get())) {
       ADD_FAILURE() << "Could not encode name";
       return {};
diff --git a/fuzz/der_roundtrip.cc b/fuzz/der_roundtrip.cc
index ffce3b9..5df58ff 100644
--- a/fuzz/der_roundtrip.cc
+++ b/fuzz/der_roundtrip.cc
@@ -29,11 +29,9 @@
     // correctly.
     size_t consumed = len - CBS_len(&cbs);
     bssl::ScopedCBB cbb;
-    CBB body_cbb;
     if (!CBB_init(cbb.get(), consumed) ||
-        !CBB_add_asn1(cbb.get(), &body_cbb, tag) ||
-        !CBB_add_bytes(&body_cbb, CBS_data(&body), CBS_len(&body)) ||
-        !CBB_flush(cbb.get()) ||
+        !CBB_add_asn1_element(cbb.get(), tag, CBS_data(&body),
+                              CBS_len(&body)) ||
         CBB_len(cbb.get()) != consumed ||
         memcmp(CBB_data(cbb.get()), buf, consumed) != 0) {
       abort();
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index f4dae0f..1738c08 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -601,6 +601,12 @@
 // child's contents nor the length prefix will be included in the output.
 OPENSSL_EXPORT void CBB_discard_child(CBB *cbb);
 
+// CBB_add_asn1_element adds an ASN.1 element with the specified tag and
+// contents. It returns one on success and zero on error. This is a convenience
+// function over |CBB_add_asn1| when the data is already available.
+OPENSSL_EXPORT int CBB_add_asn1_element(CBB *cbb, CBS_ASN1_TAG tag,
+                                        const uint8_t *data, size_t data_len);
+
 // CBB_add_asn1_uint64 writes an ASN.1 INTEGER into |cbb| using |CBB_add_asn1|
 // and writes |value| in its contents. It returns one on success and zero on
 // error.
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index 4cee993..a24d741 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -910,9 +910,9 @@
   }
 
   if (!hints->server_random_tls13.empty()) {
-    if (!CBB_add_asn1(&seq, &child, kServerRandomTLS13Tag) ||
-        !CBB_add_bytes(&child, hints->server_random_tls13.data(),
-                       hints->server_random_tls13.size())) {
+    if (!CBB_add_asn1_element(&seq, kServerRandomTLS13Tag,
+                              hints->server_random_tls13.data(),
+                              hints->server_random_tls13.size())) {
       return 0;
     }
   }
@@ -944,9 +944,9 @@
   }
 
   if (!hints->decrypted_psk.empty()) {
-    if (!CBB_add_asn1(&seq, &child, kDecryptedPSKTag) ||
-        !CBB_add_bytes(&child, hints->decrypted_psk.data(),
-                       hints->decrypted_psk.size())) {
+    if (!CBB_add_asn1_element(&seq, kDecryptedPSKTag,
+                              hints->decrypted_psk.data(),
+                              hints->decrypted_psk.size())) {
       return 0;
     }
   }
@@ -971,9 +971,9 @@
   }
 
   if (!hints->server_random_tls12.empty()) {
-    if (!CBB_add_asn1(&seq, &child, kServerRandomTLS12Tag) ||
-        !CBB_add_bytes(&child, hints->server_random_tls12.data(),
-                       hints->server_random_tls12.size())) {
+    if (!CBB_add_asn1_element(&seq, kServerRandomTLS12Tag,
+                              hints->server_random_tls12.data(),
+                              hints->server_random_tls12.size())) {
       return 0;
     }
   }
@@ -992,9 +992,9 @@
 
 
   if (!hints->decrypted_ticket.empty()) {
-    if (!CBB_add_asn1(&seq, &child, kDecryptedTicketTag) ||
-        !CBB_add_bytes(&child, hints->decrypted_ticket.data(),
-                       hints->decrypted_ticket.size())) {
+    if (!CBB_add_asn1_element(&seq, kDecryptedTicketTag,
+                              hints->decrypted_ticket.data(),
+                              hints->decrypted_ticket.size())) {
       return 0;
     }
   }
diff --git a/ssl/ssl_asn1.cc b/ssl/ssl_asn1.cc
index 037d34b..484dc9c 100644
--- a/ssl/ssl_asn1.cc
+++ b/ssl/ssl_asn1.cc
@@ -168,9 +168,8 @@
   // serialized instead.
   if (sk_CRYPTO_BUFFER_num(in->certs.get()) > 0 && !in->peer_sha256_valid) {
     const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs.get(), 0);
-    if (!CBB_add_asn1(&session, &child, kPeerTag) ||
-        !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer),
-                       CRYPTO_BUFFER_len(buffer))) {
+    if (!CBB_add_asn1_element(&session, kPeerTag, CRYPTO_BUFFER_data(buffer),
+                              CRYPTO_BUFFER_len(buffer))) {
       return 0;
     }
   }