| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef NET_SSL_CHANNEL_ID_SERVICE_H_ |
| #define NET_SSL_CHANNEL_ID_SERVICE_H_ |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/threading/non_thread_safe.h" |
| #include "base/time/time.h" |
| #include "net/base/completion_callback.h" |
| #include "net/base/net_export.h" |
| #include "net/ssl/channel_id_store.h" |
| |
| namespace base { |
| class TaskRunner; |
| } |
| |
| namespace net { |
| |
| class ChannelIDServiceJob; |
| class ChannelIDServiceRequest; |
| class ChannelIDServiceWorker; |
| |
| // A class for creating and fetching domain bound certs. They are used |
| // to identify users' machines; their public keys are used as channel IDs in |
| // http://tools.ietf.org/html/draft-balfanz-tls-channelid-00. |
| // As a result although certs are set to be invalid after one year, we don't |
| // actually expire them. Once generated, certs are valid as long as the users |
| // want. Users can delete existing certs, and new certs will be generated |
| // automatically. |
| |
| // Inherits from NonThreadSafe in order to use the function |
| // |CalledOnValidThread|. |
| class NET_EXPORT ChannelIDService |
| : NON_EXPORTED_BASE(public base::NonThreadSafe) { |
| public: |
| class NET_EXPORT RequestHandle { |
| public: |
| RequestHandle(); |
| ~RequestHandle(); |
| |
| // Cancel the request. Does nothing if the request finished or was already |
| // cancelled. |
| void Cancel(); |
| |
| bool is_active() const { return request_ != NULL; } |
| |
| private: |
| friend class ChannelIDService; |
| |
| void RequestStarted(ChannelIDService* service, |
| ChannelIDServiceRequest* request, |
| const CompletionCallback& callback); |
| |
| void OnRequestComplete(int result); |
| |
| ChannelIDService* service_; |
| ChannelIDServiceRequest* request_; |
| CompletionCallback callback_; |
| }; |
| |
| // Password used on EncryptedPrivateKeyInfo data stored in EC private_key |
| // values. (This is not used to provide any security, but to workaround NSS |
| // being unable to import unencrypted PrivateKeyInfo for EC keys.) |
| static const char kEPKIPassword[]; |
| |
| // This object owns |channel_id_store|. |task_runner| will |
| // be used to post certificate generation worker tasks. The tasks are |
| // safe for use with WorkerPool and SequencedWorkerPool::CONTINUE_ON_SHUTDOWN. |
| ChannelIDService( |
| ChannelIDStore* channel_id_store, |
| const scoped_refptr<base::TaskRunner>& task_runner); |
| |
| ~ChannelIDService(); |
| |
| // Returns the domain to be used for |host|. The domain is the |
| // "registry controlled domain", or the "ETLD + 1" where one exists, or |
| // the origin otherwise. |
| static std::string GetDomainForHost(const std::string& host); |
| |
| // Tests whether the system time is within the supported range for |
| // certificate generation. This value is cached when ChannelIDService |
| // is created, so if the system time is changed by a huge amount, this may no |
| // longer hold. |
| bool IsSystemTimeValid() const { return is_system_time_valid_; } |
| |
| // Fetches the domain bound cert for the specified host if one exists and |
| // creates one otherwise. Returns OK if successful or an error code upon |
| // failure. |
| // |
| // On successful completion, |private_key| stores a DER-encoded |
| // PrivateKeyInfo struct, and |cert| stores a DER-encoded certificate. |
| // The PrivateKeyInfo is always an ECDSA private key. |
| // |
| // |callback| must not be null. ERR_IO_PENDING is returned if the operation |
| // could not be completed immediately, in which case the result code will |
| // be passed to the callback when available. |
| // |
| // |*out_req| will be initialized with a handle to the async request. This |
| // RequestHandle object must be cancelled or destroyed before the |
| // ChannelIDService is destroyed. |
| int GetOrCreateChannelID( |
| const std::string& host, |
| std::string* private_key, |
| std::string* cert, |
| const CompletionCallback& callback, |
| RequestHandle* out_req); |
| |
| // Fetches the domain bound cert for the specified host if one exists. |
| // Returns OK if successful, ERR_FILE_NOT_FOUND if none exists, or an error |
| // code upon failure. |
| // |
| // On successful completion, |private_key| stores a DER-encoded |
| // PrivateKeyInfo struct, and |cert| stores a DER-encoded certificate. |
| // The PrivateKeyInfo is always an ECDSA private key. |
| // |
| // |callback| must not be null. ERR_IO_PENDING is returned if the operation |
| // could not be completed immediately, in which case the result code will |
| // be passed to the callback when available. If an in-flight |
| // GetChannelID is pending, and a new GetOrCreateDomainBoundCert |
| // request arrives for the same domain, the GetChannelID request will |
| // not complete until a new cert is created. |
| // |
| // |*out_req| will be initialized with a handle to the async request. This |
| // RequestHandle object must be cancelled or destroyed before the |
| // ChannelIDService is destroyed. |
| int GetChannelID( |
| const std::string& host, |
| std::string* private_key, |
| std::string* cert, |
| const CompletionCallback& callback, |
| RequestHandle* out_req); |
| |
| // Returns the backing ChannelIDStore. |
| ChannelIDStore* GetChannelIDStore(); |
| |
| // Public only for unit testing. |
| int cert_count(); |
| uint64 requests() const { return requests_; } |
| uint64 cert_store_hits() const { return cert_store_hits_; } |
| uint64 inflight_joins() const { return inflight_joins_; } |
| uint64 workers_created() const { return workers_created_; } |
| |
| private: |
| // Cancels the specified request. |req| is the handle stored by |
| // GetChannelID(). After a request is canceled, its completion |
| // callback will not be called. |
| void CancelRequest(ChannelIDServiceRequest* req); |
| |
| void GotChannelID(int err, |
| const std::string& server_identifier, |
| base::Time expiration_time, |
| const std::string& key, |
| const std::string& cert); |
| void GeneratedChannelID( |
| const std::string& server_identifier, |
| int error, |
| scoped_ptr<ChannelIDStore::ChannelID> channel_id); |
| void HandleResult(int error, |
| const std::string& server_identifier, |
| const std::string& private_key, |
| const std::string& cert); |
| |
| // Searches for an in-flight request for the same domain. If found, |
| // attaches to the request and returns true. Returns false if no in-flight |
| // request is found. |
| bool JoinToInFlightRequest(const base::TimeTicks& request_start, |
| const std::string& domain, |
| std::string* private_key, |
| std::string* cert, |
| bool create_if_missing, |
| const CompletionCallback& callback, |
| RequestHandle* out_req); |
| |
| // Looks for the domain bound cert for |domain| in this service's store. |
| // Returns OK if it can be found synchronously, ERR_IO_PENDING if the |
| // result cannot be obtained synchronously, or a network error code on |
| // failure (including failure to find a domain-bound cert of |domain|). |
| int LookupChannelID(const base::TimeTicks& request_start, |
| const std::string& domain, |
| std::string* private_key, |
| std::string* cert, |
| bool create_if_missing, |
| const CompletionCallback& callback, |
| RequestHandle* out_req); |
| |
| scoped_ptr<ChannelIDStore> channel_id_store_; |
| scoped_refptr<base::TaskRunner> task_runner_; |
| |
| // inflight_ maps from a server to an active generation which is taking |
| // place. |
| std::map<std::string, ChannelIDServiceJob*> inflight_; |
| |
| uint64 requests_; |
| uint64 cert_store_hits_; |
| uint64 inflight_joins_; |
| uint64 workers_created_; |
| |
| bool is_system_time_valid_; |
| |
| base::WeakPtrFactory<ChannelIDService> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ChannelIDService); |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_SSL_CHANNEL_ID_SERVICE_H_ |