package com.secureline.server;

import android.util.Slog;

import org.json.JSONObject;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;

public class SecureLineCommandVerifier {

    private static final String TAG =
            "SecureLineCommandVerifier";

    // DER encoded ECDSA public key (prime256v1)
    private static final String SERVER_PUBLIC_KEY_HEX =
    "3059301306072a8648ce3d020106082a8648ce3d0301070342000415a371e7e53dbb92df8c4432dcc4533aeb5476f60e54b98a0f158a6303b5bd4556a326a7a4684e0c2756ac58833feef7b0e1139505fa8897f00fd51f1dea1f36";

    private final PublicKey mServerKey;

    public SecureLineCommandVerifier() {

        try {

            byte[] der =
                    hexToBytes(
                            SERVER_PUBLIC_KEY_HEX
                    );

            mServerKey =
                    KeyFactory
                            .getInstance("EC")
                            .generatePublic(
                                    new X509EncodedKeySpec(
                                            der
                                    )
                            );

        } catch (Exception e) {

            throw new RuntimeException(
                    "Command public key load failed",
                    e
            );
        }
    }

    public JSONObject verify(

            String payloadBase64,
            String signatureBase64

    ) throws Exception {

        byte[] payload =
                android.util.Base64.decode(
                        payloadBase64,
                        android.util.Base64.URL_SAFE
                                | android.util.Base64.NO_WRAP
                );

        byte[] signature =
                android.util.Base64.decode(
                        signatureBase64,
                        android.util.Base64.URL_SAFE
                                | android.util.Base64.NO_WRAP
                );

        Signature sig =
                Signature.getInstance(
                        "SHA256withECDSA"
                );

        sig.initVerify(
                mServerKey
        );

        sig.update(
                payload
        );

        if (!sig.verify(signature)) {

            throw new SecurityException(
                    "Command signature invalid"
            );
        }

        JSONObject command =
                new JSONObject(

                        new String(
                                payload,
                                StandardCharsets.UTF_8
                        )
                );

        Slog.i(TAG, "Command signature OK");

        return command;
    }

    public JSONObject verify(

            byte[] payload,
            byte[] signature

    ) throws Exception {

        String payloadBase64 =
                android.util.Base64.encodeToString(
                        payload,
                        android.util.Base64.URL_SAFE
                                | android.util.Base64.NO_WRAP
                );

        String signatureBase64 =
                android.util.Base64.encodeToString(
                        signature,
                        android.util.Base64.URL_SAFE
                                | android.util.Base64.NO_WRAP
                );

        return verify(payloadBase64, signatureBase64);
    }

    private static byte[] hexToBytes(
            String hex
    ) {

        int len =
                hex.length();

        byte[] out =
                new byte[len / 2];

        for (int i = 0;
             i < len;
             i += 2) {

            out[i / 2] =
                    (byte) (

                            (Character.digit(
                                    hex.charAt(i),
                                    16
                            ) << 4)

                                    +

                                    Character.digit(
                                            hex.charAt(i + 1),
                                            16
                                    )

                    );
        }

        return out;
    }
}
