File
Blob: src/workerd/api/crypto/spkac.c++
| 1 | #include "spkac.h" |
| 2 | |
| 3 | #include "impl.h" |
| 4 | |
| 5 | #include <workerd/io/io-context.h> |
| 6 | #include <workerd/jsg/jsg.h> |
| 7 | |
| 8 | #include <ncrypto.h> |
| 9 | |
| 10 | namespace workerd::api { |
| 11 | |
| 12 | bool verifySpkac(kj::ArrayPtr<const kj::byte> input) { |
| 13 | // So, this is fun. SPKAC uses MD5 as the digest algorithm. This is a problem because |
| 14 | // using MD5 for signature verification is not allowed in FIPS mode, which means that |
| 15 | // although we have a working implementation here, the result of this call is always |
| 16 | // going to be false even if the input signature is correct. So this is a bit of a dead |
| 17 | // end that isn't going to be super useful. Fortunately tho the exportPublicKey and |
| 18 | // exportChallenge functions both work correctly and are useful. Unfortunately, this |
| 19 | // likely means users would need to implement their own verification, which sucks. |
| 20 | // |
| 21 | // Alternatively we could choose to implement our own version of the validation that |
| 22 | // bypasses BoringSSL's FIPS configuration. For now tho, this does end up matching |
| 23 | // Node.js' behavior when FIPS is enabled so I guess that's something. |
| 24 | KJ_IF_SOME(ioContext, IoContext::tryCurrent()) { |
| 25 | ioContext.logWarningOnce( |
| 26 | "The verifySpkac function is currently of limited value in workers because " |
| 27 | "the SPKAC signature verification uses MD5, which is not supported in FIPS mode. " |
| 28 | "All workers run in FIPS mode. Accordingly, this method will currently always " |
| 29 | "return false even if the SPKAC signature is valid. This is a known limitation."); |
| 30 | } |
| 31 | |
| 32 | // Works around a bug in ncrypto... |
| 33 | auto pos = std::string_view(input.asChars().begin(), input.size()).find_last_not_of(" \n\r\t"); |
| 34 | if (pos == std::string_view::npos) { |
| 35 | return false; |
| 36 | } |
| 37 | |
| 38 | return ncrypto::VerifySpkac(ToNcryptoBuffer(input.asChars())); |
| 39 | } |
| 40 | |
| 41 | kj::Maybe<jsg::JsUint8Array> exportPublicKey(jsg::Lock& js, kj::ArrayPtr<const kj::byte> input) { |
| 42 | // Works around a bug in ncrypto... |
| 43 | auto pos = std::string_view(input.asChars().begin(), input.size()).find_last_not_of(" \n\r\t"); |
| 44 | if (pos == std::string_view::npos) { |
| 45 | return kj::none; |
| 46 | } |
| 47 | |
| 48 | if (auto bio = ncrypto::ExportPublicKey(ToNcryptoBuffer(input.asChars()))) { |
| 49 | BUF_MEM* bptr = bio; |
| 50 | auto buf = jsg::JsUint8Array::create(js, bptr->length); |
| 51 | auto aptr = kj::arrayPtr(bptr->data, bptr->length); |
| 52 | buf.asArrayPtr<char>().copyFrom(aptr); |
| 53 | return buf; |
| 54 | } |
| 55 | return kj::none; |
| 56 | } |
| 57 | |
| 58 | kj::Maybe<jsg::JsUint8Array> exportChallenge(jsg::Lock& js, kj::ArrayPtr<const kj::byte> input) { |
| 59 | |
| 60 | // Works around a bug in ncrypto... |
| 61 | auto pos = std::string_view(input.asChars().begin(), input.size()).find_last_not_of(" \n\r\t"); |
| 62 | if (pos == std::string_view::npos) { |
| 63 | return kj::none; |
| 64 | } |
| 65 | |
| 66 | if (auto dp = ncrypto::ExportChallenge(ToNcryptoBuffer(input.asChars()))) { |
| 67 | auto src = kj::arrayPtr(dp.get<kj::byte>(), dp.size()); |
| 68 | return jsg::JsUint8Array::create(js, src); |
| 69 | } |
| 70 | return kj::none; |
| 71 | } |
| 72 | } // namespace workerd::api |