
function delete_keys()
{
  localStorage.setItem("private_key","");
  localStorage.setItem("public_key","");
}

function arrayBufferToBase64(arrayBuffer)
{
  var byteArray = new Uint8Array(arrayBuffer);
  var byteString = '';
  for(var i=0; i < byteArray.byteLength; i++) {
      byteString += String.fromCharCode(byteArray[i]);
  }
  var b64 = window.btoa(byteString);
  return b64;
}

function addNewLines(str)
{
  var finalString = '';
  while(str.length > 0) {
      finalString += str.substring(0, 64) + '\n';
      str = str.substring(64);
  }
  return finalString;
}

function toPem(key,keytype)
{
  var b64 = addNewLines(arrayBufferToBase64(key));
  var pem = "-----BEGIN "+keytype+" KEY-----\n" + b64 + "-----END "+keytype+" KEY-----";
  pem=btoa(pem); // base64_encode
  return pem;
}

// für Import usw:
function pemToArrayBuffer(pem)
{
  var b64Lines = removeLines(pem);
  var b64Lines = b64Lines.replace('-----BEGIN PUBLIC KEY-----', '');
  var b64Lines = b64Lines.replace('-----END PUBLIC KEY-----', '');
  var b64Lines = b64Lines.replace('-----BEGIN PRIVATE KEY-----', '');
  var b64Lines = b64Lines.replace('-----END PRIVATE KEY-----', '');
  return base64ToArrayBuffer(b64Lines);
}

function base64ToArrayBuffer(b64)
{
  var byteString = window.atob(b64);
  var byteArray = new Uint8Array(byteString.length);
  for(var i=0; i < byteString.length; i++) {
      byteArray[i] = byteString.charCodeAt(i);
  }
  return byteArray;
}

function removeLines(str)
{
  return str.replace("\n", "");
}


let iv;
let ciphertext;

/*
Encrypt the message using the secret key.
Update the "ciphertextValue" box with a representation of part of
the ciphertext.
*/
async function encrypt(secretKey)
{
  if(!secretKey){return false;}
  // secretKey=atob(document.data.sharedkey.value);

  const ciphertextValue = document.eingabe.cipher;
  ciphertextValue.value = "";

  iv = window.crypto.getRandomValues(new Uint8Array(16));

  // IV speichern:
  document.eingabe.iv.value=arrayBufferToBase64(iv);

  let message = _("eingabefeld").value.trim();
  if(message=="")return false;

  let encoded = new TextEncoder().encode(message);

  ciphertext = await window.crypto.subtle.encrypt(
    {
      name: "AES-CBC",
      iv: iv
    },
    secretKey,
    encoded
  );
  ciphertextValue.value = arrayBufferToBase64(ciphertext);
  return true;
}

/*
Decrypt the message using the secret key.
If the ciphertext was decrypted successfully,
update the "decryptedValue" box with the decrypted value.
If there was an error decrypting,
update the "decryptedValue" box with an error message.
*/
async function decrypt_c(iv_id,secretKey) // entschlüsselt alle msgs im mittlere Tab
{
  const push_id=iv_id.replace("push_iv_","");

  const cipherfeld=_('push_encrypted_'+push_id);
  const textfeld_klein=_('push_text_klein_'+push_id);
  const textfeld_gross=_('push_text_gross_'+push_id);
  const textfeld_orig=_('push_orig_'+push_id);
  const ivfeld=_('push_iv_'+push_id);

  ciphertext=cipherfeld.innerHTML;

  iv=_(ivfeld).innerHTML;

  if(iv=="" || !iv)return false; // raus!

  iv=base64ToArrayBuffer(iv);

  if(iv.length!=16)return false; // raus!

  try {
    let decrypted = await window.crypto.subtle.decrypt(
      {
        name: "AES-CBC",
        iv: iv
      },
      secretKey,
      base64ToArrayBuffer(ciphertext)
    );

    let dec = new TextDecoder();
    push_text=dec.decode(decrypted);
    push_text_orig=strip_tags(bereinigen(push_text));
    push_text_klein=nl2br(replace_buttons(replace_smilies(replace_fett(replace_links(strip_tags(bereinigen(push_text)))))));
    push_text_gross=push_text_klein;
    // dann erst kleinen kürzen:
    if(push_text_klein.length>500)push_text_klein=push_text_klein.substring(0,500)+"...<br><a href='javascript:void(0);'>more</a>";

    textfeld_orig.innerHTML = push_text_orig;
    textfeld_klein.innerHTML = push_text_klein;
    textfeld_gross.innerHTML = push_text_gross;

    // IV leer setzen, damit noch nochmal decrypted wird!
    _(ivfeld).innerHTML=""; //leeren!

    // weil der Chat größer wurde durch das encryptete:
    // scrollzu('unten');

   } catch (e) {
    // decryptedValue.innerHTML = "*** Decryption error ***";
   }
}

async function decrypt_all_c()
{
  pubpartner=document.data.public_key_partner.value;
  if(pubpartner=="")return;

  shared_key=await get_shared_key(pubpartner);
  // alle IVs suchen und Chats decrypten:
  alle_ivs=_('.push_iv');
  for(i=0;i<alle_ivs.length;i++)
  {
    if(_(alle_ivs[i].id).innerHTML!="")
    {
      try
      {
        await decrypt_c(alle_ivs[i].id,shared_key);
      }
      catch(e)
      {
        console.log('FEHLER bei Push '+alle_ivs[i].id);
        _(alle_ivs[i].id).innerHTML=""; //leeren!
      }
    }
  }
}

async function decrypt_l(push_id,secretKey) // entschlüsselt alle "last_pushs" im linken Tab
{
  iv_id="last_push_iv_"+push_id;

  const decryptedValue = _("last_push_text_"+push_id);

  ciphertext=_("last_push_encrypted_"+push_id).innerHTML;

  iv=base64ToArrayBuffer(_(iv_id).innerHTML);
  if(iv.length!=16)return false;

  // try {
    let decrypted = await window.crypto.subtle.decrypt(
      {
        name: "AES-CBC",
        iv: iv
      },
      secretKey,
      base64ToArrayBuffer(ciphertext)
    );

    let dec = new TextDecoder();
    last_push_text=dec.decode(decrypted);
    if(last_push_text.length>25)last_push_text=last_push_text.substring(0,35)+"...";

    last_push_text=replace_smilies(strip_tags(bereinigen(last_push_text)));

    decryptedValue.innerHTML = last_push_text;

    // IV leer setzen, damit nicht nochmal decrypted wird!
    //leeren(iv_id); // IV leeren!

  // } catch (e) {
  //   decryptedValue.innerHTML = "*** Decryption error ***";
  // }
}

async function decrypt_all_l()
{
  // Interval stoppen:
  // window.clearInterval(intv_decrypt_all_l);

  // alle IVs suchen auf der linken Seite:
  alle_ivs=_('.last_push_data');

  for(i=0;i<alle_ivs.length;i++)
  {
    const theid=alle_ivs[i].id.replace("last_push_data_","");

    if(!_("last_push_pubkey_"+theid))continue;

    // Werte holen:
    iv=alle_ivs[i].innerHTML;
    pubpartner=_("last_push_pubkey_"+theid).innerHTML;
    text=_("last_push_encrypted_"+theid).innerHTML;
    is_decrypted=_("last_push_decrypted_"+theid).innerHTML;

    if(iv && pubpartner && text && is_decrypted=="0")
    {
      try
      {
        shared_key=await get_shared_key(pubpartner);
        try
        {
          // console.log('SKOA:',theid);
          decrypt_l(theid,shared_key);
          // if(_("last_push_decrypted_"+theid))
          _("last_push_decrypted_"+theid).innerHTML="1";
        }
        catch(e)
        {
          // if(_("last_push_decrypted_"+theid))
          _("last_push_decrypted_"+theid).innerHTML="0";
        }
      }
      catch(e)
      {
        // if(_("last_push_decrypted_"+theid))
        _("last_push_decrypted_"+theid).innerHTML="0";
      }
    }
  }
  window.setTimeout("decrypt_all_l();",1); // ohne Timeout gehts NICHT!
}

/*
Derive an AES key, given:
- our ECDH private key
- their ECDH public key
*/
function deriveSecretKey(privateKey, publicKey)
{
  return window.crypto.subtle.deriveKey(
    {
      name: "ECDH",
      public: publicKey
    },
    privateKey,
    {
      name: "AES-CBC",
      length: 256
    },
    false,
    ["encrypt", "decrypt"]
  );
}

async function get_shared_key(public_key_partner)
{
  if(localStorage.getItem("private_key")=="")
  {
    // alert('noch keinen eigenen Private-Key!');
    return false;
  }

  // erst Keys importieren:
  aliceprivkey=pemToArrayBuffer(atob(localStorage.getItem("private_key")));
  alicepubkey=pemToArrayBuffer(atob(localStorage.getItem("public_key")));

  if(public_key_partner=="")
  {
    alert("Der Partner hat noch keinen Public-Key generiert!");
    return false;
  }
  // Bob: Public Key importieren:
  bobpubkey=pemToArrayBuffer(atob(public_key_partner));

  let importedPublicKeyBoB = await window.crypto.subtle.importKey(
      "spki",
      bobpubkey,
      {
          name: "ECDH",
          namedCurve: "P-521"
      },
      false,
      []
  );

  let importedPrivateKey = await window.crypto.subtle.importKey(
    "pkcs8",
    aliceprivkey,
    {
        name: "ECDH",
        namedCurve: "P-521"
    },
    false,
    ["deriveKey"]
  );

  // Alice then generates a secret key using her private key and Bob's public key.
  //let alicesSecretKey = await deriveSecretKey(importedPrivateKey, importedPublicKeyBoB);
  return await deriveSecretKey(importedPrivateKey, importedPublicKeyBoB);
}
