// 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_