Support PBES2 schemes in PKCS12_create

We were able to decrypt them, but not create them. This adds support for
creating them. They are a little goofy because OpenSSL has two different
conventions for specifying PBES schemes. Decryption just requires
parsing out the AlgorithmIdentifier structure embedded in the input,
while encryption requires some way for the caller to specify the
algorithms.

PKCS#12 files could be encrypted with two different PBES (password-based
encryption scheme) schemes from PKCS#5: PBES1 and PBES2. Where PBES1 was
fully described by a single OID, PBES2 is itself parameterized by a KDF
and a cipher[*].

OpenSSL first added PBES2 encrypting support in
https://github.com/openssl/openssl/commit/8eb57af5fed7a133cdb2a968081c355249c00b98,
but only added it to PKCS8_encrypt, for encrypting PKCS#8 blobs. This
was early so they were willing to break their public APIs. They chose a
convention where you passed both a NID and an EVP_CIPHER. If the NID was
-1, the cipher would be used instead with PBES2, hardcoding PBKDF2 with,
at the time, HMAC-SHA1. We implement this API.

Later, OpenSSL added PBES2 encrypting to PKCS12_create in
https://github.com/openssl/openssl/commit/b0e69a05008531c51180cf8fc10694871d33eaa8,
but now backwards compatibility does not allow adding an EVP_CIPHER
parameter to PKCS12_create. OpenSSL instead decided that if the NID
matched a known EVP_CIPHER (e.g. NID_aes_256_cbc), it would implicitly
pick PBES2, the way -1 and an EVP_CIPHER specified it before.

This CL implements that behavior. I've opted to keep the PKCS8_encrypt
calling convention and just translate to it for now. But we really
should pick a less chaotic calling convention (we're C++ now, so we
could even use std::variant...) at least for the internals.

For now, the defaults are still at the values they were in older
OpenSSL. I've filed https://crbug.com/396434682 to track that. (Only
reason to do it separately is to have something easily revertible and
decide when to do the low-but-nonzero-risk change.)

As an aside, OpenSSL later further complicated this (still not
documenting anything) with
https://github.com/openssl/openssl/commit/5693a30813a031d3921a016a870420e7eb93ec90

There was no way to specify the PBKDF2 PRF function. So they overloaded
the NID parameter, to specify that. Now the decision tree is:

- If the NID is -1, PBES2 with the cipher and the default PBKDF2
- If the NID is a PRF algorithm, PBES2 with the cipher and that PRF as
  the PBKDF2 PRF
- Otherwise, PBES1 with the NID specifying the PBES1 scheme

This CL does not implement that extra feature. This calling convention
is a mess.

[*] To be honest, I'm not sure what PBES2 even specifies. The operation
in RFC 8018, Section 6.2.1 is pretty much meaningless. Use the selected
KDF to derive a key, then use the selected cipher to encrypt it. PBES2
does not actually come with any choices of KDF or cipher, you have to
pick them when formulating a PBES2 scheme. PBES2 appears to just
describe sticking the two together generically.

Fixed: 394337104
Change-Id: I1380b339d398783ee65cde3d9f526b9d8fc1c2d4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/76307
Commit-Queue: David Benjamin <davidben@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
6 files changed
tree: 027a42cbc343f8373d3a1ac05ddf797eaa35cfe2
  1. .bcr/
  2. .github/
  3. cmake/
  4. crypto/
  5. decrepit/
  6. docs/
  7. fuzz/
  8. gen/
  9. include/
  10. infra/
  11. pki/
  12. rust/
  13. ssl/
  14. third_party/
  15. tool/
  16. util/
  17. .bazelignore
  18. .bazelrc
  19. .bazelversion
  20. .clang-format
  21. .gitignore
  22. API-CONVENTIONS.md
  23. AUTHORS
  24. BREAKING-CHANGES.md
  25. BUILD.bazel
  26. build.json
  27. BUILDING.md
  28. CMakeLists.txt
  29. codereview.settings
  30. CONTRIBUTING.md
  31. FUZZING.md
  32. go.mod
  33. go.sum
  34. INCORPORATING.md
  35. LICENSE
  36. MODULE.bazel
  37. MODULE.bazel.lock
  38. PORTING.md
  39. PrivacyInfo.xcprivacy
  40. README.md
  41. SANDBOXING.md
  42. STYLE.md
README.md

BoringSSL

BoringSSL is a fork of OpenSSL that is designed to meet Google's needs.

Although BoringSSL is an open source project, it is not intended for general use, as OpenSSL is. We don't recommend that third parties depend upon it. Doing so is likely to be frustrating because there are no guarantees of API or ABI stability.

Programs ship their own copies of BoringSSL when they use it and we update everything as needed when deciding to make API changes. This allows us to mostly avoid compromises in the name of compatibility. It works for us, but it may not work for you.

BoringSSL arose because Google used OpenSSL for many years in various ways and, over time, built up a large number of patches that were maintained while tracking upstream OpenSSL. As Google's product portfolio became more complex, more copies of OpenSSL sprung up and the effort involved in maintaining all these patches in multiple places was growing steadily.

Currently BoringSSL is the SSL library in Chrome/Chromium, Android (but it's not part of the NDK) and a number of other apps/programs.

Project links:

To file a security issue, use the Chromium process and mention in the report this is for BoringSSL. You can ignore the parts of the process that are specific to Chromium/Chrome.

There are other files in this directory which might be helpful: