blob: e4b8701362eb346050332aac1a63941c077cb7c9 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 HERMES_ESIM_H_
#define HERMES_ESIM_H_
#include <libqrtr.h>
#include <cstdint>
#include <deque>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/callback.h>
#include <base/files/scoped_file.h>
#include <base/memory/weak_ptr.h>
#include <base/message_loop/message_loop.h>
#include "hermes/qmi_constants.h"
#include "hermes/qmi_uim.h"
namespace hermes {
// EsimError will be the generic abstraction from specific protocol errors to an
// error that the interface can understand. This should contain a set of errors
// that are recoverable, meaning if they occur, the process can still complete
// successfully. This should also include errors that are fatal, and should be
// reported back to the user.
enum class EsimError {
kEsimSuccess, // success condition
kEsimError, // fatal
kEsimNotConnected, // non-fatal
};
// Provides the functionality to allow the LPD to communicate with the eUICC.
// This class is responsible for opening, maintaining, and closing the logical
// channel that will be opened to the chip.
class Esim : public base::MessageLoopForIO::Watcher {
public:
using ErrorCallback = base::Callback<void(EsimError error_data)>;
using DataCallback =
base::Callback<void(const std::vector<uint8_t>& esim_data)>;
static std::unique_ptr<Esim> Create(std::string imei,
std::string matching_id);
static std::unique_ptr<Esim> CreateForTest(base::ScopedFD* sock,
std::string imei,
std::string matching_id);
// Sets up FileDescriptorWatcher and transaction handling. Gets node and port
// for UIM service from QRTR. Calls |success_callback| upon successful
// intialization, |error_callback| on error.
//
// Parameters
// success_callback - Closure to continue execution after eSIM is
// initialized
// error_callack - function to handle error if initialization fails
void Initialize(const base::Closure& success_callback,
const ErrorCallback& error_callback);
~Esim() = default;
// Makes eSIM API call to request the eSIM to open a logical channel to
// communicate through. This will be the method through which all two way
// communication will be dealt with the hardware. Calls |data_callback|
// on successful open channel, or |error_callback| on error.
//
// Parameters
// data_callback - function to call on successful channel opening
// error_callback - function to handle error returned from eSIM
void OpenLogicalChannel(const DataCallback& data_callback,
const ErrorCallback& error_callback);
// Makes eSIM API call to request the eSIM to return either the info1 or
// the info2 block of data to send to the SM-DP+ server to begin
// Authentication. Calls |callback| with the newly returned data, or
// |error_callback| on error.
//
// Parameters
// which - specify either the info1 or info2 data
// callback - function to call on successful return of specified eSIM
// info data
// error_callback - function to handle error returned from eSIM
void GetInfo(int which,
const DataCallback& data_callback,
const ErrorCallback& error_callback);
// Makes eSIM API call to request the eSIM to return the eSIM Challenge,
// which is the second parameter needed to begin Authentication with the
// SM-DP+ server. Calls |callback| on returned challenge, or |error_callback|
// on error.
//
// Parameters
// callback - function to call on successful return of eSIM challenge
// data blob
// error_callback - function to handle error returned from eSIM
void GetChallenge(const DataCallback& data_callback,
const ErrorCallback& error_callback);
// Makes eSIM API call to request eSIM to authenticate server's signature,
// which is supplied in server_signature. On success, call |callback| with
// the eSIM's response. If the authentication fails, call |error_callback|.
//
// Parameters
// server_signed1 - data that has been signed with
// |server_signature| server_signature SM-DP+
// encryption signature
// euicc_ci_pk_id_to_be_used - list of public keys for the eSIM to choose
// server_certificate - SM-DP+ certificate
// data_callback - function to call with the data returned from
// the eSIM on successful authentication of
// |server_data|
// error_callback - function to call if |server_data| is
// determined to be invalid by eSIM chip
void AuthenticateServer(const std::vector<uint8_t>& server_signed1,
const std::vector<uint8_t>& server_signature,
const std::vector<uint8_t>& euicc_ci_pk_id_to_be_used,
const std::vector<uint8_t>& server_certificate,
const DataCallback& data_callback,
const ErrorCallback& error_callback);
void PrepareDownloadRequest(const std::vector<uint8_t>& smdp_signed2,
const std::vector<uint8_t>& smdp_signature2,
const std::vector<uint8_t>& smdp_certificate,
const DataCallback& data_callback,
const ErrorCallback& error_callback);
void LoadBoundProfilePackage(
const std::vector<uint8_t>& bound_profile_package,
const DataCallback& success_callback,
const ErrorCallback& error_callback);
private:
// TODO(jruthe): IMEI is hardware specific and should be retrieved from the
// hardware configuration
Esim(uint8_t slot,
const std::string& imei,
const std::string& matching_id,
base::ScopedFD fd);
void CloseChannel();
void SendApdu(const DataCallback& callback,
const ErrorCallback& error_callback);
// APDUs have a payload size limit of 255 bytes. Thus payloads that are
// greater than 255 bytes, must be fragmented into several APDUs that are
// sent sequentially. Each time a payload of 255 bytes is constructed, it is
// appended to |apdu_queue_|. If |apdu_payload| is smaller than 255 bytes, it
// is simply given a header and appended to |apdu_queue_|.
//
// Parameters
// cla - CLA byte as defined in ISO 7816 5.1.1
// ins - INS byte as defined in ISO 7816 5.1.2
// apdu_payload - Buffer containing an APDU payload
void FragmentAndQueueApdu(uint8_t cla,
uint8_t ins,
const std::vector<uint8_t>& apdu_payload);
// Messages sent between the Lpd and the eSIM chip are done through a series
// of transactions. The Lpd can intiate transactions and the eSIM will
// respond with a matching transaction number to the Lpd's request.
//
// This pair of functions map a set of callbacks data and error to the current
// value of |current_transaction_|. The eSIM will respond after some amount of
// time and the Lpd will read the bytes from the socket opened to the QRTR
// socket. Finalize transaction will handle the reception of APDUs and map
// them back to transaction numbers by doing lookups into
// |response_callbacks_|.
//
// Parameters
// packet - Encoded QRTR packet to send via qrtr_sendto.
// data_callback - Callback to call once corresponding transaction has been
// received and processed.
// error_callback - Error handler after transaction has been received but
// encounters an error during processing.
void InitiateTransaction(const qrtr_packet& packet,
const DataCallback& data_callback,
const ErrorCallback& error_callback);
// Parameters
// packet - QRTR packet to decode and process.
void FinalizeTransaction(const qrtr_packet& packet);
// Build CtxParams object described in SGP.22 5.7.13 including matchingId,
// deviceInfo and their respective parameters.
//
// Parameters
// ctx_params - vector of bytes that will be filled with ctx_params endoded
// in ASN.1 format. ctx_params is not modified if the
// construction fails
//
// Returns - True if ctx_params are constructed, false otherwise.
bool ConstructCtxParams(std::vector<uint8_t>* ctx_params);
uint16_t GetTransactionNumber(const qrtr_packet& packet) const;
bool ResponseSuccess(uint16_t response) const;
bool MorePayloadIncoming() const;
uint8_t GetNextPayloadSize() const;
bool StringToBcdBytes(const std::string& source, std::vector<uint8_t>* dest);
void QueueStoreData(const std::vector<uint8_t>& payload);
// base::MessageLoopForIO::Watcher overrides.
void OnFileCanReadWithoutBlocking(int fd) override;
void OnFileCanWriteWithoutBlocking(int fd) override;
// FileDescriptorWatcher to watch the QRTR socket for reads.
base::MessageLoopForIO::FileDescriptorWatcher watcher_;
// Counter for each initiated transaction.
uint16_t current_transaction_;
// IMEI number
std::string imei_;
// Matching ID of profile to install
std::string matching_id_;
struct TransactionCallback {
TransactionCallback() = default;
TransactionCallback(const DataCallback& data_callback,
const ErrorCallback& error_callback);
DataCallback data_callback;
ErrorCallback error_callback;
};
// Mapping of transactions to callbacks from Lpd once the eSIM has responded
// to a request.
std::map<int, TransactionCallback> response_callbacks_;
// Queue of completed packets to send to the eSIM, these will be sent one at
// a time through Esim::SendApdu as success responses are received.
std::deque<std::vector<uint8_t> > apdu_queue_;
// The slot that the logical channel to the eSIM will be made. Initialized in
// constructor, hardware specific.
uint8_t slot_;
// Buffer for storing data from the QRTR socket.
std::vector<uint8_t> buffer_;
// Node and port to pass to qrtr_sendto, returned from qrtr_new_lookup
// response, which contains QRTR_TYPE_NEW_SERVER and the node and port number
// to use in following qrtr_sendto calls.
uint32_t node_;
uint32_t port_;
// Logical Channel that will be used to communicate with the chip, returned
// from OPEN_LOGICAL_CHANNEL request sent once the QRTR socket has been
// opened.
uint8_t channel_;
// Status returned as the last two bytes in APDU messages
uint8_t sw1_;
uint8_t sw2_;
// All APDU bytes. sw1_ and sw2_ are extracted immediately after reception.
std::vector<uint8_t> payload_;
// Closure given to Esim::Initialize to call once the UIM service has been
// exposed through QRTR and |node_| and |port_| have been set.
base::Closure initialize_callback_;
// ScopedFD to hold the qrtr socket file descriptor returned by qrtr_open.
// Initialized in Create or CreateForTest, which will be passed to the
// constructor, ScopedFD will be destructed with Esim object.
base::ScopedFD qrtr_socket_fd_;
base::WeakPtrFactory<Esim> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Esim);
};
} // namespace hermes
#endif // HERMES_ESIM_H_