#include #include #include #include #include #include #include #include namespace workerd::server { ActorIdFactoryImpl::ActorIdImpl::ActorIdImpl( const kj::byte idParam[SHA256_DIGEST_LENGTH], kj::Maybe name) : name(kj::mv(name)) { memcpy(id, idParam, sizeof(id)); } kj::String ActorIdFactoryImpl::ActorIdImpl::toString() const { return kj::encodeHex(kj::ArrayPtr(id)); } kj::Maybe ActorIdFactoryImpl::ActorIdImpl::getName() const { return name; } kj::Maybe ActorIdFactoryImpl::ActorIdImpl::getJurisdiction() const { return kj::none; } bool ActorIdFactoryImpl::ActorIdImpl::equals(const ActorId& other) const { return kj::arrayPtr(id) == kj::arrayPtr(kj::downcast(other).id); } kj::Own ActorIdFactoryImpl::ActorIdImpl::clone() const { return kj::heap(id, mapCopyString(name)); } ActorIdFactoryImpl::ActorIdFactoryImpl(kj::StringPtr uniqueKey) { KJ_ASSERT(SHA256(uniqueKey.asBytes().begin(), uniqueKey.size(), key) == key); } ActorIdFactoryImpl::ActorIdFactoryImpl(const kj::byte keyParam[SHA256_DIGEST_LENGTH]) { memcpy(key, keyParam, sizeof(key)); } kj::Own ActorIdFactoryImpl::newUniqueId( kj::Maybe jurisdiction) { JSG_REQUIRE( jurisdiction == kj::none, Error, "Jurisdiction restrictions are not implemented in workerd."); // We want to randomly-generate the first 16 bytes, then HMAC those to produce the latter // 16 bytes. But the HMAC will produce 32 bytes, so we're only taking a prefix of it. We'll // allocate a single array big enough to output the HMAC as a suffix, which will then get // truncated. kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]{}; if (isPredictableModeForTest()) { memcpy(id, &counter, sizeof(counter)); kj::arrayPtr(id).slice(counter).fill(0); ++counter; } else { getEntropy(kj::arrayPtr(id, BASE_LENGTH)); } computeMac(id); return kj::heap(id, kj::none); } kj::Own ActorIdFactoryImpl::idFromName(kj::String name) { kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]{}; // Compute the first half of the ID by HMACing the name itself. We're using HMAC as a keyed // hash here, not actually for authentication, but it works. unsigned int len = SHA256_DIGEST_LENGTH; KJ_ASSERT( HMAC(EVP_sha256(), key, sizeof(key), name.asBytes().begin(), name.size(), id, &len) == id); KJ_ASSERT(len == SHA256_DIGEST_LENGTH); computeMac(id); return kj::heap(id, kj::mv(name)); } kj::Own ActorIdFactoryImpl::idFromString(kj::String str) { auto decoded = kj::decodeHex(str); JSG_REQUIRE(str.size() == SHA256_DIGEST_LENGTH * 2 && !decoded.hadErrors && decoded.size() == SHA256_DIGEST_LENGTH, TypeError, "Invalid Durable Object ID: must be 64 hex digits"); kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]{}; memcpy(id, decoded.begin(), BASE_LENGTH); computeMac(id); // Verify that the computed mac matches the input. JSG_REQUIRE(kj::arrayPtr(id).slice(BASE_LENGTH).startsWith(decoded.asPtr().slice(BASE_LENGTH)), TypeError, "Durable Object ID is not valid for this namespace."); return kj::heap(id, kj::none); } kj::Own ActorIdFactoryImpl::cloneWithJurisdiction( kj::Maybe maybeJurisdiction) { if (maybeJurisdiction == kj::none) { return kj::heap(key); } JSG_FAIL_REQUIRE(Error, "Jurisdiction restrictions are not implemented in workerd."); } bool ActorIdFactoryImpl::matchesJurisdiction(const ActorId& id) { return true; } void ActorIdFactoryImpl::computeMac(kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]) { // Given that the first `BASE_LENGTH` bytes of `id` are filled in, compute the second half // of the ID by HMACing the first half. The id must be in a buffer large enough to store the // first half of the ID plus a full HMAC, even though only a prefix of the HMAC becomes part // of the final ID. kj::byte* hmacOut = id + BASE_LENGTH; unsigned int len = SHA256_DIGEST_LENGTH; KJ_ASSERT(HMAC(EVP_sha256(), key, sizeof(key), id, BASE_LENGTH, hmacOut, &len) == hmacOut); KJ_ASSERT(len == SHA256_DIGEST_LENGTH); } } //namespace workerd::server