Skip to content
File

Blob: src/workerd/server/actor-id-impl.c++

4.4 KB
1#include <workerd/jsg/exception.h>
2#include <workerd/server/actor-id-impl.h>
3#include <workerd/util/entropy.h>
4#include <workerd/util/own-util.h>
5#include <workerd/util/thread-scopes.h>
6 
7#include <openssl/hmac.h>
8 
9#include <kj/encoding.h>
10#include <kj/memory.h>
11 
12namespace workerd::server {
13 
14ActorIdFactoryImpl::ActorIdImpl::ActorIdImpl(
15 const kj::byte idParam[SHA256_DIGEST_LENGTH], kj::Maybe<kj::String> name)
16 : name(kj::mv(name)) {
17 memcpy(id, idParam, sizeof(id));
18}
19 
20kj::String ActorIdFactoryImpl::ActorIdImpl::toString() const {
21 return kj::encodeHex(kj::ArrayPtr<const kj::byte>(id));
22}
23 
24kj::Maybe<kj::StringPtr> ActorIdFactoryImpl::ActorIdImpl::getName() const {
25 return name;
26}
27 
28kj::Maybe<kj::StringPtr> ActorIdFactoryImpl::ActorIdImpl::getJurisdiction() const {
29 return kj::none;
30}
31 
32bool ActorIdFactoryImpl::ActorIdImpl::equals(const ActorId& other) const {
33 return kj::arrayPtr(id) == kj::arrayPtr(kj::downcast<const ActorIdImpl>(other).id);
34}
35 
36kj::Own<ActorIdFactory::ActorId> ActorIdFactoryImpl::ActorIdImpl::clone() const {
37 return kj::heap<ActorIdImpl>(id, mapCopyString(name));
38}
39 
40ActorIdFactoryImpl::ActorIdFactoryImpl(kj::StringPtr uniqueKey) {
41 KJ_ASSERT(SHA256(uniqueKey.asBytes().begin(), uniqueKey.size(), key) == key);
42}
43 
44ActorIdFactoryImpl::ActorIdFactoryImpl(const kj::byte keyParam[SHA256_DIGEST_LENGTH]) {
45 memcpy(key, keyParam, sizeof(key));
46}
47 
48kj::Own<ActorIdFactory::ActorId> ActorIdFactoryImpl::newUniqueId(
49 kj::Maybe<kj::StringPtr> jurisdiction) {
50 JSG_REQUIRE(
51 jurisdiction == kj::none, Error, "Jurisdiction restrictions are not implemented in workerd.");
52 
53 // We want to randomly-generate the first 16 bytes, then HMAC those to produce the latter
54 // 16 bytes. But the HMAC will produce 32 bytes, so we're only taking a prefix of it. We'll
55 // allocate a single array big enough to output the HMAC as a suffix, which will then get
56 // truncated.
57 kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]{};
58 
59 if (isPredictableModeForTest()) {
60 memcpy(id, &counter, sizeof(counter));
61 kj::arrayPtr(id).slice(counter).fill(0);
62 ++counter;
63 } else {
64 getEntropy(kj::arrayPtr(id, BASE_LENGTH));
65 }
66 
67 computeMac(id);
68 return kj::heap<ActorIdImpl>(id, kj::none);
69}
70 
71kj::Own<ActorIdFactory::ActorId> ActorIdFactoryImpl::idFromName(kj::String name) {
72 kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]{};
73 
74 // Compute the first half of the ID by HMACing the name itself. We're using HMAC as a keyed
75 // hash here, not actually for authentication, but it works.
76 unsigned int len = SHA256_DIGEST_LENGTH;
77 KJ_ASSERT(
78 HMAC(EVP_sha256(), key, sizeof(key), name.asBytes().begin(), name.size(), id, &len) == id);
79 KJ_ASSERT(len == SHA256_DIGEST_LENGTH);
80 
81 computeMac(id);
82 return kj::heap<ActorIdImpl>(id, kj::mv(name));
83}
84 
85kj::Own<ActorIdFactory::ActorId> ActorIdFactoryImpl::idFromString(kj::String str) {
86 auto decoded = kj::decodeHex(str);
87 JSG_REQUIRE(str.size() == SHA256_DIGEST_LENGTH * 2 && !decoded.hadErrors &&
88 decoded.size() == SHA256_DIGEST_LENGTH,
89 TypeError, "Invalid Durable Object ID: must be 64 hex digits");
90 
91 kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]{};
92 memcpy(id, decoded.begin(), BASE_LENGTH);
93 computeMac(id);
94 
95 // Verify that the computed mac matches the input.
96 JSG_REQUIRE(kj::arrayPtr(id).slice(BASE_LENGTH).startsWith(decoded.asPtr().slice(BASE_LENGTH)),
97 TypeError, "Durable Object ID is not valid for this namespace.");
98 
99 return kj::heap<ActorIdImpl>(id, kj::none);
100}
101 
102kj::Own<ActorIdFactory> ActorIdFactoryImpl::cloneWithJurisdiction(
103 kj::Maybe<kj::StringPtr> maybeJurisdiction) {
104 if (maybeJurisdiction == kj::none) {
105 return kj::heap<ActorIdFactoryImpl>(key);
106 }
107 
108 JSG_FAIL_REQUIRE(Error, "Jurisdiction restrictions are not implemented in workerd.");
109}
110 
111bool ActorIdFactoryImpl::matchesJurisdiction(const ActorId& id) {
112 return true;
113}
114 
115void ActorIdFactoryImpl::computeMac(kj::byte id[BASE_LENGTH + SHA256_DIGEST_LENGTH]) {
116 // Given that the first `BASE_LENGTH` bytes of `id` are filled in, compute the second half
117 // of the ID by HMACing the first half. The id must be in a buffer large enough to store the
118 // first half of the ID plus a full HMAC, even though only a prefix of the HMAC becomes part
119 // of the final ID.
120 
121 kj::byte* hmacOut = id + BASE_LENGTH;
122 unsigned int len = SHA256_DIGEST_LENGTH;
123 KJ_ASSERT(HMAC(EVP_sha256(), key, sizeof(key), id, BASE_LENGTH, hmacOut, &len) == hmacOut);
124 KJ_ASSERT(len == SHA256_DIGEST_LENGTH);
125}
126 
127} //namespace workerd::server