/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <stddef.h>
#include <stdint.h>

#if !defined(__INTRODUCED_IN)
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif

// These APIs are for the Adb pairing protocol. This protocol requires both
// sides to possess a shared secret to authenticate each other. The connection
// is over TLS, and requires that both the client and server have a valid
// certificate.
//
// This protocol is one-to-one, i.e., one PairingConnectionCtx server instance
// interacts with only one PairingConnectionCtx client instance. In other words,
// every new client instance must be bound to a new server instance.
//
// If both sides have authenticated, they will exchange their peer information
// (see #PeerInfo).
#ifdef __cplusplus
extern "C" {
#endif

const uint32_t kMaxPeerInfoSize = 8192;
struct PeerInfo {
    uint8_t type;
    uint8_t data[kMaxPeerInfoSize - 1];
} __attribute__((packed));
typedef struct PeerInfo PeerInfo;
static_assert(sizeof(PeerInfo) == kMaxPeerInfoSize, "PeerInfo has weird size");

enum PeerInfoType : uint8_t {
    ADB_RSA_PUB_KEY = 0,
    ADB_DEVICE_GUID = 1,
};

struct PairingConnectionCtx;
typedef struct PairingConnectionCtx PairingConnectionCtx;
typedef void (*pairing_result_cb)(const PeerInfo*, int, void*);

// Starts the pairing connection on a separate thread.
//
// Upon completion, if the pairing was successful,
// |cb| will be called with the peer information and certificate.
// Otherwise, |cb| will be called with empty data. |fd| should already
// be opened. PairingConnectionCtx will take ownership of the |fd|.
//
// Pairing is successful if both server/client uses the same non-empty
// |pswd|, and they are able to exchange the information. |pswd| and
// |certificate| must be non-empty. start() can only be called once in the
// lifetime of this object.
//
// @param ctx the PairingConnectionCtx instance. Will abort if null.
// @param fd the fd connecting the peers. This will take ownership of fd.
// @param cb the user-provided callback that is called with the result of the
//        pairing. The callback will be called on a different thread from the
//        caller.
// @param opaque opaque userdata.
// @return true if the thread was successfully started, false otherwise. To stop
//         the connection process, destroy the instance (see
//         #pairing_connection_destroy). If false is returned, cb will not be
//         invoked. Otherwise, cb is guaranteed to be invoked, even if you
//         destroy the ctx while in the pairing process.
bool pairing_connection_start(PairingConnectionCtx* ctx, int fd, pairing_result_cb cb, void* opaque)
        __INTRODUCED_IN(30);

// Creates a new PairingConnectionCtx instance as the client.
//
// @param pswd the password to authenticate both peers. Will abort if null.
// @param pswd_len the length of pswd. Will abort if 0.
// @param peer_info the PeerInfo struct that is exchanged between peers if the
//                  pairing was successful. Will abort if null.
// @param x509_cert_pem the X.509 certificate in PEM format. Will abort if null.
// @param x509_size the size of x509_cert_pem. Will abort if 0.
// @param priv_key_pem the private key corresponding to the given X.509
//                     certificate, in PEM format. Will abort if null.
// @param priv_size the size of priv_key_pem. Will abort if 0.
// @return a new PairingConnectionCtx client instance. The caller is responsible
//         for destroying the context via #pairing_connection_destroy.
PairingConnectionCtx* pairing_connection_client_new(const uint8_t* pswd, size_t pswd_len,
                                                    const PeerInfo* peer_info,
                                                    const uint8_t* x509_cert_pem, size_t x509_size,
                                                    const uint8_t* priv_key_pem, size_t priv_size)
        __INTRODUCED_IN(30);

// Creates a new PairingConnectionCtx instance as the server.
//
// @param pswd the password to authenticate both peers. Will abort if null.
// @param pswd_len the length of pswd. Will abort if 0.
// @param peer_info the PeerInfo struct that is exchanged between peers if the
//                  pairing was successful. Will abort if null.
// @param x509_cert_pem the X.509 certificate in PEM format. Will abort if null.
// @param x509_size the size of x509_cert_pem. Will abort if 0.
// @param priv_key_pem the private key corresponding to the given X.509
//                     certificate, in PEM format. Will abort if null.
// @param priv_size the size of priv_key_pem. Will abort if 0.
// @return a new PairingConnectionCtx server instance. The caller is responsible
//         for destroying the context via #pairing_connection_destroy.
PairingConnectionCtx* pairing_connection_server_new(const uint8_t* pswd, size_t pswd_len,
                                                    const PeerInfo* peer_info,
                                                    const uint8_t* x509_cert_pem, size_t x509_size,
                                                    const uint8_t* priv_key_pem, size_t priv_size)
        __INTRODUCED_IN(30);

// Destroys the PairingConnectionCtx instance.
//
// It is safe to destroy the instance at any point in the pairing process.
//
// @param ctx the PairingConnectionCtx instance to destroy. Will abort if null.
void pairing_connection_destroy(PairingConnectionCtx* ctx) __INTRODUCED_IN(30);

#ifdef __cplusplus
}
#endif
