Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: chrome/browser/chromeos/platform_keys/platform_keys_nss.cc

Issue 430563002: Enable system token in platformKeys api. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased. Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/platform_keys/platform_keys.h" 5 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
6 6
7 #include <cryptohi.h> 7 #include <cryptohi.h>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 // The task runner on which the NSS operation was called. Any reply must be 65 // The task runner on which the NSS operation was called. Any reply must be
66 // posted to this runner. 66 // posted to this runner.
67 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; 67 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
68 68
69 private: 69 private:
70 DISALLOW_COPY_AND_ASSIGN(NSSOperationState); 70 DISALLOW_COPY_AND_ASSIGN(NSSOperationState);
71 }; 71 };
72 72
73 typedef base::Callback<void(net::NSSCertDatabase* cert_db)> GetCertDBCallback; 73 typedef base::Callback<void(net::NSSCertDatabase* cert_db)> GetCertDBCallback;
74 74
75 // Called back with the NSSCertDatabase associated to the given |token_id|. 75 // Used by GetCertDatabaseOnIOThread and called back with the requested
76 // Calls |callback| if the database was successfully retrieved. Used by 76 // NSSCertDatabase.
77 // GetCertDatabaseOnIOThread. 77 // If |token_id| is not empty, sets |slot_| of |state| accordingly and calls
78 void DidGetCertDBOnIOThread(const GetCertDBCallback& callback, 78 // |callback| if the database was successfully retrieved.
79 void DidGetCertDBOnIOThread(const std::string& token_id,
80 const GetCertDBCallback& callback,
79 NSSOperationState* state, 81 NSSOperationState* state,
80 net::NSSCertDatabase* cert_db) { 82 net::NSSCertDatabase* cert_db) {
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
82 if (!cert_db) { 84 if (!cert_db) {
83 LOG(ERROR) << "Couldn't get NSSCertDatabase."; 85 LOG(ERROR) << "Couldn't get NSSCertDatabase.";
84 state->OnError(FROM_HERE, kErrorInternal); 86 state->OnError(FROM_HERE, kErrorInternal);
85 return; 87 return;
86 } 88 }
87 89
88 state->slot_ = cert_db->GetPrivateSlot(); 90 if (!token_id.empty()) {
89 if (!state->slot_) { 91 if (token_id == kTokenIdUser)
90 LOG(ERROR) << "No private slot"; 92 state->slot_ = cert_db->GetPrivateSlot();
91 state->OnError(FROM_HERE, kErrorInternal); 93 else if (token_id == kTokenIdSystem)
92 return; 94 state->slot_ = cert_db->GetSystemSlot();
95
96 if (!state->slot_) {
97 LOG(ERROR) << "Slot for token id '" << token_id << "' not available.";
98 state->OnError(FROM_HERE, kErrorInternal);
99 return;
100 }
93 } 101 }
94 102
95 callback.Run(cert_db); 103 callback.Run(cert_db);
96 } 104 }
97 105
98 // Retrieves the NSSCertDatabase from |context|. Must be called on the IO 106 // Retrieves the NSSCertDatabase from |context| and, if |token_id| is not empty,
99 // thread. 107 // the slot for |token_id|.
100 void GetCertDatabaseOnIOThread(content::ResourceContext* context, 108 // Must be called on the IO thread.
109 void GetCertDatabaseOnIOThread(const std::string& token_id,
101 const GetCertDBCallback& callback, 110 const GetCertDBCallback& callback,
111 content::ResourceContext* context,
102 NSSOperationState* state) { 112 NSSOperationState* state) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
104 net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext( 114 net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext(
105 context, base::Bind(&DidGetCertDBOnIOThread, callback, state)); 115 context, base::Bind(&DidGetCertDBOnIOThread, token_id, callback, state));
106 116
107 if (cert_db) 117 if (cert_db)
108 DidGetCertDBOnIOThread(callback, state, cert_db); 118 DidGetCertDBOnIOThread(token_id, callback, state, cert_db);
109 } 119 }
110 120
111 // Asynchronously fetches the NSSCertDatabase and PK11Slot for |token_id|. 121 // Asynchronously fetches the NSSCertDatabase for |browser_context| and, if
112 // Stores the slot in |state| and passes the database to |callback|. Will run 122 // |token_id| is not empty, the slot for |token_id|. Stores the slot in |state|
113 // |callback| on the IO thread. 123 // and passes the database to |callback|. Will run |callback| on the IO thread.
114 void GetCertDatabase(const std::string& token_id, 124 void GetCertDatabase(const std::string& token_id,
115 const GetCertDBCallback& callback, 125 const GetCertDBCallback& callback,
116 BrowserContext* browser_context, 126 BrowserContext* browser_context,
117 NSSOperationState* state) { 127 NSSOperationState* state) {
118 // TODO(pneubeck): Decide which DB to retrieve depending on |token_id|.
119 BrowserThread::PostTask(BrowserThread::IO, 128 BrowserThread::PostTask(BrowserThread::IO,
120 FROM_HERE, 129 FROM_HERE,
121 base::Bind(&GetCertDatabaseOnIOThread, 130 base::Bind(&GetCertDatabaseOnIOThread,
131 token_id,
132 callback,
122 browser_context->GetResourceContext(), 133 browser_context->GetResourceContext(),
123 callback,
124 state)); 134 state));
125 } 135 }
126 136
127 class GenerateRSAKeyState : public NSSOperationState { 137 class GenerateRSAKeyState : public NSSOperationState {
128 public: 138 public:
129 GenerateRSAKeyState(unsigned int modulus_length_bits, 139 GenerateRSAKeyState(unsigned int modulus_length_bits,
130 const subtle::GenerateKeyCallback& callback); 140 const subtle::GenerateKeyCallback& callback);
131 virtual ~GenerateRSAKeyState() {} 141 virtual ~GenerateRSAKeyState() {}
132 142
133 virtual void OnError(const tracked_objects::Location& from, 143 virtual void OnError(const tracked_objects::Location& from,
134 const std::string& error_message) OVERRIDE { 144 const std::string& error_message) OVERRIDE {
135 CallBack(from, std::string() /* no public key */, error_message); 145 CallBack(from, std::string() /* no public key */, error_message);
136 } 146 }
137 147
138 void CallBack(const tracked_objects::Location& from, 148 void CallBack(const tracked_objects::Location& from,
139 const std::string& public_key_spki_der, 149 const std::string& public_key_spki_der,
140 const std::string& error_message) { 150 const std::string& error_message) {
141 origin_task_runner_->PostTask( 151 origin_task_runner_->PostTask(
142 from, base::Bind(callback_, public_key_spki_der, error_message)); 152 from, base::Bind(callback_, public_key_spki_der, error_message));
143 } 153 }
144 154
145 const unsigned int modulus_length_bits_; 155 const unsigned int modulus_length_bits_;
146 156
147 private: 157 private:
148 // Must be called on origin thread, use CallBack() therefore. 158 // Must be called on origin thread, therefore use CallBack().
149 subtle::GenerateKeyCallback callback_; 159 subtle::GenerateKeyCallback callback_;
150 }; 160 };
151 161
152 class SignState : public NSSOperationState { 162 class SignState : public NSSOperationState {
153 public: 163 public:
154 SignState(const std::string& public_key, 164 SignState(const std::string& public_key,
155 HashAlgorithm hash_algorithm, 165 HashAlgorithm hash_algorithm,
156 const std::string& data, 166 const std::string& data,
157 const subtle::SignCallback& callback); 167 const subtle::SignCallback& callback);
158 virtual ~SignState() {} 168 virtual ~SignState() {}
159 169
160 virtual void OnError(const tracked_objects::Location& from, 170 virtual void OnError(const tracked_objects::Location& from,
161 const std::string& error_message) OVERRIDE { 171 const std::string& error_message) OVERRIDE {
162 CallBack(from, std::string() /* no signature */, error_message); 172 CallBack(from, std::string() /* no signature */, error_message);
163 } 173 }
164 174
165 void CallBack(const tracked_objects::Location& from, 175 void CallBack(const tracked_objects::Location& from,
166 const std::string& signature, 176 const std::string& signature,
167 const std::string& error_message) { 177 const std::string& error_message) {
168 origin_task_runner_->PostTask( 178 origin_task_runner_->PostTask(
169 from, base::Bind(callback_, signature, error_message)); 179 from, base::Bind(callback_, signature, error_message));
170 } 180 }
171 181
172 const std::string public_key_; 182 const std::string public_key_;
173 HashAlgorithm hash_algorithm_; 183 HashAlgorithm hash_algorithm_;
174 const std::string data_; 184 const std::string data_;
175 185
176 private: 186 private:
177 // Must be called on origin thread, use CallBack() therefore. 187 // Must be called on origin thread, therefore use CallBack().
178 subtle::SignCallback callback_; 188 subtle::SignCallback callback_;
179 }; 189 };
180 190
181 class GetCertificatesState : public NSSOperationState { 191 class GetCertificatesState : public NSSOperationState {
182 public: 192 public:
183 explicit GetCertificatesState(const GetCertificatesCallback& callback); 193 explicit GetCertificatesState(const GetCertificatesCallback& callback);
184 virtual ~GetCertificatesState() {} 194 virtual ~GetCertificatesState() {}
185 195
186 virtual void OnError(const tracked_objects::Location& from, 196 virtual void OnError(const tracked_objects::Location& from,
187 const std::string& error_message) OVERRIDE { 197 const std::string& error_message) OVERRIDE {
188 CallBack(from, 198 CallBack(from,
189 scoped_ptr<net::CertificateList>() /* no certificates */, 199 scoped_ptr<net::CertificateList>() /* no certificates */,
190 error_message); 200 error_message);
191 } 201 }
192 202
193 void CallBack(const tracked_objects::Location& from, 203 void CallBack(const tracked_objects::Location& from,
194 scoped_ptr<net::CertificateList> certs, 204 scoped_ptr<net::CertificateList> certs,
195 const std::string& error_message) { 205 const std::string& error_message) {
196 origin_task_runner_->PostTask( 206 origin_task_runner_->PostTask(
197 from, base::Bind(callback_, base::Passed(&certs), error_message)); 207 from, base::Bind(callback_, base::Passed(&certs), error_message));
198 } 208 }
199 209
200 scoped_ptr<net::CertificateList> certs_; 210 scoped_ptr<net::CertificateList> certs_;
201 211
202 private: 212 private:
203 // Must be called on origin thread, use CallBack() therefore. 213 // Must be called on origin thread, therefore use CallBack().
204 GetCertificatesCallback callback_; 214 GetCertificatesCallback callback_;
205 }; 215 };
206 216
207 class ImportCertificateState : public NSSOperationState { 217 class ImportCertificateState : public NSSOperationState {
208 public: 218 public:
209 ImportCertificateState(scoped_refptr<net::X509Certificate> certificate, 219 ImportCertificateState(scoped_refptr<net::X509Certificate> certificate,
210 const ImportCertificateCallback& callback); 220 const ImportCertificateCallback& callback);
211 virtual ~ImportCertificateState() {} 221 virtual ~ImportCertificateState() {}
212 222
213 virtual void OnError(const tracked_objects::Location& from, 223 virtual void OnError(const tracked_objects::Location& from,
214 const std::string& error_message) OVERRIDE { 224 const std::string& error_message) OVERRIDE {
215 CallBack(from, error_message); 225 CallBack(from, error_message);
216 } 226 }
217 227
218 void CallBack(const tracked_objects::Location& from, 228 void CallBack(const tracked_objects::Location& from,
219 const std::string& error_message) { 229 const std::string& error_message) {
220 origin_task_runner_->PostTask(from, base::Bind(callback_, error_message)); 230 origin_task_runner_->PostTask(from, base::Bind(callback_, error_message));
221 } 231 }
222 232
223 scoped_refptr<net::X509Certificate> certificate_; 233 scoped_refptr<net::X509Certificate> certificate_;
224 234
225 private: 235 private:
226 // Must be called on origin thread, use CallBack() therefore. 236 // Must be called on origin thread, therefore use CallBack().
227 ImportCertificateCallback callback_; 237 ImportCertificateCallback callback_;
228 }; 238 };
229 239
230 class RemoveCertificateState : public NSSOperationState { 240 class RemoveCertificateState : public NSSOperationState {
231 public: 241 public:
232 RemoveCertificateState(scoped_refptr<net::X509Certificate> certificate, 242 RemoveCertificateState(scoped_refptr<net::X509Certificate> certificate,
233 const RemoveCertificateCallback& callback); 243 const RemoveCertificateCallback& callback);
234 virtual ~RemoveCertificateState() {} 244 virtual ~RemoveCertificateState() {}
235 245
236 virtual void OnError(const tracked_objects::Location& from, 246 virtual void OnError(const tracked_objects::Location& from,
237 const std::string& error_message) OVERRIDE { 247 const std::string& error_message) OVERRIDE {
238 CallBack(from, error_message); 248 CallBack(from, error_message);
239 } 249 }
240 250
241 void CallBack(const tracked_objects::Location& from, 251 void CallBack(const tracked_objects::Location& from,
242 const std::string& error_message) { 252 const std::string& error_message) {
243 origin_task_runner_->PostTask(from, base::Bind(callback_, error_message)); 253 origin_task_runner_->PostTask(from, base::Bind(callback_, error_message));
244 } 254 }
245 255
246 scoped_refptr<net::X509Certificate> certificate_; 256 scoped_refptr<net::X509Certificate> certificate_;
247 257
248 private: 258 private:
249 // Must be called on origin thread, use CallBack() therefore. 259 // Must be called on origin thread, therefore use CallBack().
250 RemoveCertificateCallback callback_; 260 RemoveCertificateCallback callback_;
251 }; 261 };
252 262
263 class GetTokensState : public NSSOperationState {
264 public:
265 explicit GetTokensState(const GetTokensCallback& callback);
266 virtual ~GetTokensState() {}
267
268 virtual void OnError(const tracked_objects::Location& from,
269 const std::string& error_message) OVERRIDE {
270 CallBack(from,
271 scoped_ptr<std::vector<std::string> >() /* no token ids */,
272 error_message);
273 }
274
275 void CallBack(const tracked_objects::Location& from,
276 scoped_ptr<std::vector<std::string> > token_ids,
277 const std::string& error_message) {
278 origin_task_runner_->PostTask(
279 from, base::Bind(callback_, base::Passed(&token_ids), error_message));
280 }
281
282 private:
283 // Must be called on origin thread, therefore use CallBack().
284 GetTokensCallback callback_;
285 };
286
253 NSSOperationState::NSSOperationState() 287 NSSOperationState::NSSOperationState()
254 : origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) { 288 : origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
255 } 289 }
256 290
257 GenerateRSAKeyState::GenerateRSAKeyState( 291 GenerateRSAKeyState::GenerateRSAKeyState(
258 unsigned int modulus_length_bits, 292 unsigned int modulus_length_bits,
259 const subtle::GenerateKeyCallback& callback) 293 const subtle::GenerateKeyCallback& callback)
260 : modulus_length_bits_(modulus_length_bits), callback_(callback) { 294 : modulus_length_bits_(modulus_length_bits), callback_(callback) {
261 } 295 }
262 296
(...skipping 17 matching lines...) Expand all
280 const ImportCertificateCallback& callback) 314 const ImportCertificateCallback& callback)
281 : certificate_(certificate), callback_(callback) { 315 : certificate_(certificate), callback_(callback) {
282 } 316 }
283 317
284 RemoveCertificateState::RemoveCertificateState( 318 RemoveCertificateState::RemoveCertificateState(
285 scoped_refptr<net::X509Certificate> certificate, 319 scoped_refptr<net::X509Certificate> certificate,
286 const RemoveCertificateCallback& callback) 320 const RemoveCertificateCallback& callback)
287 : certificate_(certificate), callback_(callback) { 321 : certificate_(certificate), callback_(callback) {
288 } 322 }
289 323
324 GetTokensState::GetTokensState(const GetTokensCallback& callback)
325 : callback_(callback) {
326 }
327
290 // Does the actual key generation on a worker thread. Used by 328 // Does the actual key generation on a worker thread. Used by
291 // GenerateRSAKeyWithDB(). 329 // GenerateRSAKeyWithDB().
292 void GenerateRSAKeyOnWorkerThread(scoped_ptr<GenerateRSAKeyState> state) { 330 void GenerateRSAKeyOnWorkerThread(scoped_ptr<GenerateRSAKeyState> state) {
293 scoped_ptr<crypto::RSAPrivateKey> rsa_key( 331 scoped_ptr<crypto::RSAPrivateKey> rsa_key(
294 crypto::RSAPrivateKey::CreateSensitive(state->slot_.get(), 332 crypto::RSAPrivateKey::CreateSensitive(state->slot_.get(),
295 state->modulus_length_bits_)); 333 state->modulus_length_bits_));
296 if (!rsa_key) { 334 if (!rsa_key) {
297 LOG(ERROR) << "Couldn't create key."; 335 LOG(ERROR) << "Couldn't create key.";
298 state->OnError(FROM_HERE, kErrorInternal); 336 state->OnError(FROM_HERE, kErrorInternal);
299 return; 337 return;
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 const net::Error cert_status = 478 const net::Error cert_status =
441 static_cast<net::Error>(db->CheckUserCert(state->certificate_)); 479 static_cast<net::Error>(db->CheckUserCert(state->certificate_));
442 if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) { 480 if (cert_status == net::ERR_NO_PRIVATE_KEY_FOR_CERT) {
443 state->OnError(FROM_HERE, kErrorKeyNotFound); 481 state->OnError(FROM_HERE, kErrorKeyNotFound);
444 return; 482 return;
445 } else if (cert_status != net::OK) { 483 } else if (cert_status != net::OK) {
446 state->OnError(FROM_HERE, net::ErrorToString(cert_status)); 484 state->OnError(FROM_HERE, net::ErrorToString(cert_status));
447 return; 485 return;
448 } 486 }
449 487
488 // Check that the private key is in the correct slot.
489 PK11SlotInfo* slot =
490 PK11_KeyForCertExists(state->certificate_->os_cert_handle(), NULL, NULL);
491 if (slot != state->slot_) {
492 state->OnError(FROM_HERE, kErrorKeyNotFound);
493 return;
494 }
495
450 const net::Error import_status = 496 const net::Error import_status =
451 static_cast<net::Error>(db->AddUserCert(state->certificate_.get())); 497 static_cast<net::Error>(db->AddUserCert(state->certificate_.get()));
452 if (import_status != net::OK) { 498 if (import_status != net::OK) {
453 LOG(ERROR) << "Could not import certificate."; 499 LOG(ERROR) << "Could not import certificate.";
454 state->OnError(FROM_HERE, net::ErrorToString(import_status)); 500 state->OnError(FROM_HERE, net::ErrorToString(import_status));
455 return; 501 return;
456 } 502 }
457 503
458 state->CallBack(FROM_HERE, std::string() /* no error */); 504 state->CallBack(FROM_HERE, std::string() /* no error */);
459 } 505 }
(...skipping 23 matching lines...) Expand all
483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
484 // Get the pointer before base::Passed clears |state|. 530 // Get the pointer before base::Passed clears |state|.
485 scoped_refptr<net::X509Certificate> certificate = state->certificate_; 531 scoped_refptr<net::X509Certificate> certificate = state->certificate_;
486 bool certificate_found = certificate->os_cert_handle()->isperm; 532 bool certificate_found = certificate->os_cert_handle()->isperm;
487 cert_db->DeleteCertAndKeyAsync( 533 cert_db->DeleteCertAndKeyAsync(
488 certificate, 534 certificate,
489 base::Bind( 535 base::Bind(
490 &DidRemoveCertificate, base::Passed(&state), certificate_found)); 536 &DidRemoveCertificate, base::Passed(&state), certificate_found));
491 } 537 }
492 538
539 // Does the actual work to determine which tokens are available.
540 void GetTokensWithDB(scoped_ptr<GetTokensState> state,
541 net::NSSCertDatabase* cert_db) {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
543 scoped_ptr<std::vector<std::string> > token_ids(new std::vector<std::string>);
544
545 // The user's token is always available.
546 token_ids->push_back(kTokenIdUser);
547 if (cert_db->GetSystemSlot())
548 token_ids->push_back(kTokenIdSystem);
549
550 state->CallBack(FROM_HERE, token_ids.Pass(), std::string() /* no error */);
551 }
552
493 } // namespace 553 } // namespace
494 554
495 namespace subtle { 555 namespace subtle {
496 556
497 void GenerateRSAKey(const std::string& token_id, 557 void GenerateRSAKey(const std::string& token_id,
498 unsigned int modulus_length_bits, 558 unsigned int modulus_length_bits,
499 const GenerateKeyCallback& callback, 559 const GenerateKeyCallback& callback,
500 BrowserContext* browser_context) { 560 BrowserContext* browser_context) {
501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
502 scoped_ptr<GenerateRSAKeyState> state( 562 scoped_ptr<GenerateRSAKeyState> state(
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
581 NSSOperationState* state_ptr = state.get(); 641 NSSOperationState* state_ptr = state.get();
582 642
583 // The NSSCertDatabase object is not required. But in case it's not available 643 // The NSSCertDatabase object is not required. But in case it's not available
584 // we would get more informative error messages. 644 // we would get more informative error messages.
585 GetCertDatabase(token_id, 645 GetCertDatabase(token_id,
586 base::Bind(&RemoveCertificateWithDB, base::Passed(&state)), 646 base::Bind(&RemoveCertificateWithDB, base::Passed(&state)),
587 browser_context, 647 browser_context,
588 state_ptr); 648 state_ptr);
589 } 649 }
590 650
651 void GetTokens(const GetTokensCallback& callback,
652 content::BrowserContext* browser_context) {
653 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
654 scoped_ptr<GetTokensState> state(new GetTokensState(callback));
655 // Get the pointer to |state| before base::Passed releases |state|.
656 NSSOperationState* state_ptr = state.get();
657 GetCertDatabase(std::string() /* don't get any specific slot */,
658 base::Bind(&GetTokensWithDB, base::Passed(&state)),
659 browser_context,
660 state_ptr);
661 }
662
591 } // namespace platform_keys 663 } // namespace platform_keys
592 664
593 } // namespace chromeos 665 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698