Crypt-Sodium-XS

 view release on metacpan or  search on metacpan

inc/kx.xs  view on Meta::CPAN

=for documentation

Note that for kx, aliases are provided for the current default primitive
(x25519blake2b) even though libsodium does not provide algorithm-specific
functions.

=cut

MODULE = Crypt::Sodium::XS PACKAGE = Crypt::Sodium::XS::kx

void _define_constants()
  PREINIT:
  HV *stash = gv_stashpv("Crypt::Sodium::XS::kx", 0);

  PPCODE:
  newCONSTSUB(stash, "kx_PUBLICKEYBYTES", newSVuv(crypto_kx_PUBLICKEYBYTES));
  newCONSTSUB(stash, "kx_x25519blake2b_PUBLICKEYBYTES",
              newSVuv(crypto_kx_PUBLICKEYBYTES));
  newCONSTSUB(stash, "kx_SECRETKEYBYTES", newSVuv(crypto_kx_SECRETKEYBYTES));
  newCONSTSUB(stash, "kx_x25519blake2b_SECRETKEYBYTES",
              newSVuv(crypto_kx_SECRETKEYBYTES));
  newCONSTSUB(stash, "kx_SEEDBYTES", newSVuv(crypto_kx_SEEDBYTES));
  newCONSTSUB(stash, "kx_x25519blake2b_SEEDBYTES",
              newSVuv(crypto_kx_SEEDBYTES));
  newCONSTSUB(stash, "kx_SESSIONKEYBYTES", newSVuv(crypto_kx_SESSIONKEYBYTES));
  newCONSTSUB(stash, "kx_x25519blake2b_SESSIONKEYBYTES",
              newSVuv(crypto_kx_SESSIONKEYBYTES));
  newCONSTSUB(stash, "kx_PRIMITIVE", newSVpvs(crypto_kx_PRIMITIVE));

void kx_keypair(SV * seed = &PL_sv_undef, SV * flags = &PL_sv_undef)

  ALIAS:
  kx_x25519blake2b_keypair = 1

  PREINIT:
  protmem *sk_pm;
  SV *pk_sv;
  unsigned char *pk_buf;
  unsigned int sk_flags = g_protmem_default_flags_key;

  PPCODE:
  PERL_UNUSED_VAR(ix);

  SvGETMAGIC(flags);
  if (SvOK(flags))
    sk_flags = SvUV_nomg(flags);

  Newx(pk_buf, crypto_kx_PUBLICKEYBYTES + 1, unsigned char);
  if (pk_buf == NULL)
    croak("kx_keypair: Failed to allocate memory");
  pk_buf[crypto_kx_PUBLICKEYBYTES] = '\0';

  SvGETMAGIC(seed);
  if (!SvOK(seed)) {
    sk_pm = protmem_init(aTHX_ crypto_kx_SECRETKEYBYTES, sk_flags);
    if (sk_pm == NULL) {
      Safefree(pk_buf);
      croak("kx_keypair: Failed to allocate protmem");
    }

    crypto_kx_keypair(pk_buf, sk_pm->pm_ptr);

    if (protmem_release(aTHX_ sk_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      protmem_free(aTHX_ sk_pm);
      Safefree(pk_buf);
      croak("kx_keypair: Failed to release sk protmem RW");
    }
  }
  else {
    /* from seed */
    protmem *seed_pm = NULL;
    unsigned char *seed_buf;
    STRLEN seed_len;

    if (sv_derived_from(seed, MEMVAULT_CLASS)) {
      seed_pm = protmem_get(aTHX_ seed, MEMVAULT_CLASS);
      seed_buf = seed_pm->pm_ptr;
      seed_len = seed_pm->size;
    }
    else
      seed_buf = (unsigned char *)SvPVbyte_nomg(seed, seed_len);

    if (seed_len != crypto_kx_SEEDBYTES) {
      Safefree(pk_buf);
      croak("kx_keypair: Invalid seed length: %lu", seed_len);
    }

    sk_pm = protmem_init(aTHX_ crypto_kx_SECRETKEYBYTES, sk_flags);
    if (sk_pm == NULL) {
      Safefree(pk_buf);
      croak("kx_keypair: Failed to allocate protmem");
    }
    /* use flags from seed_pm (if there was one)? */

    if (seed_pm && protmem_grant(aTHX_ seed_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
      protmem_free(aTHX_ sk_pm);
      Safefree(pk_buf);
      croak("kx_keypair: Failed to grant seed protmem RO");
    }

    crypto_kx_seed_keypair(pk_buf, sk_pm->pm_ptr, seed_buf);

    if (seed_pm && protmem_release(aTHX_ seed_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
      protmem_free(aTHX_ sk_pm);
      Safefree(pk_buf);
      croak("kx_keypair: Failed to release seed protmem RO");
    }

    if (protmem_release(aTHX_ sk_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      protmem_free(aTHX_ sk_pm);
      Safefree(pk_buf);
      croak("kx_keypair: Failed to release sk protmem RW");
    }
  }

  pk_sv = newSV(0);
  sv_usepvn_flags(pk_sv, (char *)pk_buf, crypto_kx_PUBLICKEYBYTES, SV_HAS_TRAILING_NUL);
  mXPUSHs(pk_sv);
  mXPUSHs(protmem_to_sv(aTHX_ sk_pm, MEMVAULT_CLASS));
  XSRETURN(2);

void kx_client_session_keys(SV * cpk, SV * csk, SV * spk, SV * flags = &PL_sv_undef)

  ALIAS:
  kx_x25519blake2b_client_session_keys = 1

  PREINIT:
  protmem *rx, *tx, *csk_pm = NULL;
  unsigned char * cpk_buf, * csk_buf, * spk_buf;
  STRLEN cpk_len, csk_len, spk_len;
  unsigned int key_flags = g_protmem_default_flags_key;
  int ret;

  PPCODE:
  PERL_UNUSED_VAR(ix);

  SvGETMAGIC(flags);
  if (SvOK(flags))
    key_flags = SvUV_nomg(flags);

  cpk_buf = (unsigned char *)SvPVbyte(cpk, cpk_len);
  if (cpk_len != crypto_kx_PUBLICKEYBYTES)
    croak("kx_client_session_keys: Invalid public key length %lu", cpk_len);

  if (sv_derived_from(csk, MEMVAULT_CLASS)) {
    csk_pm = protmem_get(aTHX_ csk, MEMVAULT_CLASS);
    csk_buf = csk_pm->pm_ptr;
    csk_len = csk_pm->size;
  }
  else
    csk_buf = (unsigned char *)SvPVbyte(csk, csk_len);
  if (csk_len != crypto_kx_SECRETKEYBYTES)
    croak("kx_client_session_keys: Invalid secret key length %lu", csk_len);

  spk_buf = (unsigned char *)SvPVbyte(spk, spk_len);
  if (spk_len != crypto_kx_PUBLICKEYBYTES)
    croak("kx_client_session_keys: Invalid public key length %lu", spk_len);

  rx = protmem_init(aTHX_ crypto_kx_SESSIONKEYBYTES, key_flags);
  if (rx == NULL)
    croak("kx_client_session_keys: Failed to allocate rx protmem");
  if (csk_pm)
    rx->flags = csk_pm->flags;

  tx = protmem_init(aTHX_ crypto_kx_SESSIONKEYBYTES, key_flags);
  if (tx == NULL) {
    protmem_free(aTHX_ rx);
    croak("kx_client_session_keys: Failed to allocate tx protmem");
  }
  if (csk_pm)
    tx->flags = csk_pm->flags;

  if (csk_pm)
    if (protmem_grant(aTHX_ csk_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
      protmem_free(aTHX_ rx);
      protmem_free(aTHX_ tx);
      croak("kx_client_session_keys: Failed to grant csk protmem RO");
    }

  ret = crypto_kx_client_session_keys(rx->pm_ptr, tx->pm_ptr,
                                      cpk_buf, csk_buf, spk_buf);

  if (csk_pm && protmem_release(aTHX_ csk_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ rx);
    protmem_free(aTHX_ tx);
    croak("kx_client_session_keys: Failed to release csk protmem RO");
  }

  if (protmem_release(aTHX_ rx, PROTMEM_FLAG_MPROTECT_RW) != 0
      || protmem_release(aTHX_ tx, PROTMEM_FLAG_MPROTECT_RW) != 0) {
    protmem_free(aTHX_ rx);
    protmem_free(aTHX_ tx);
    croak("kx_client_session_keys: Failed to release rx/tx protmem RW");
  }

  if (ret != 0) {
    protmem_free(aTHX_ rx);
    protmem_free(aTHX_ tx);
    croak("kx_client_session_keys: Failed to generate session keys (invalid server pubkey?)");
  }

  mXPUSHs(protmem_to_sv(aTHX_ rx, MEMVAULT_CLASS));
  mXPUSHs(protmem_to_sv(aTHX_ tx, MEMVAULT_CLASS));
  XSRETURN(2);

void kx_server_session_keys(SV * spk, SV * ssk, SV * cpk, SV * flags = &PL_sv_undef)

  ALIAS:
  kx_x25519blake2b_server_session_keys = 1

  PREINIT:
  protmem *rx, *tx, *ssk_pm = NULL;
  unsigned char * spk_buf, * ssk_buf, * cpk_buf;
  STRLEN spk_len, ssk_len, cpk_len;
  unsigned int key_flags = g_protmem_default_flags_key;
  int ret;

  PPCODE:
  PERL_UNUSED_VAR(ix);

  SvGETMAGIC(flags);
  if (SvOK(flags))
    key_flags = SvUV_nomg(flags);

  spk_buf = (unsigned char *)SvPVbyte(spk, spk_len);
  if (spk_len != crypto_kx_PUBLICKEYBYTES)
    croak("kx_server_session_keys: Invalid public key length %lu", spk_len);

  if (sv_derived_from(ssk, MEMVAULT_CLASS)) {
    ssk_pm = protmem_get(aTHX_ ssk, MEMVAULT_CLASS);
    ssk_buf = ssk_pm->pm_ptr;
    ssk_len = ssk_pm->size;
  }
  else
    ssk_buf = (unsigned char *)SvPVbyte(ssk, ssk_len);
  if (ssk_len != crypto_kx_SECRETKEYBYTES)
    croak("kx_server_session_keys: Invalid secret key length %lu", ssk_len);

  cpk_buf = (unsigned char *)SvPVbyte(cpk, cpk_len);
  if (cpk_len != crypto_kx_PUBLICKEYBYTES)
    croak("kx_server_session_keys: Invalid public key length %lu", cpk_len);

  rx = protmem_init(aTHX_ crypto_kx_SESSIONKEYBYTES, key_flags);
  if (rx == NULL)
    croak("kx_server_session_keys: Failed to allocate rx protmem");
  if (ssk_pm)
    rx->flags = ssk_pm->flags;

  tx = protmem_init(aTHX_ crypto_kx_SESSIONKEYBYTES, key_flags);
  if (tx == NULL) {
    protmem_free(aTHX_ rx);
    croak("kx_server_session_keys: Failed to allocate tx protmem");
  }
  if (ssk_pm)
    tx->flags = ssk_pm->flags;

  if (ssk_pm && protmem_grant(aTHX_ ssk_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ rx);
    protmem_free(aTHX_ tx);
    croak("kx_server_session_keys: Failed to grant ssk protmem RO");
  }

  ret = crypto_kx_server_session_keys(rx->pm_ptr, tx->pm_ptr,
                                      spk_buf, ssk_buf, cpk_buf);

  if (ssk_pm && protmem_release(aTHX_ ssk_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ rx);
    protmem_free(aTHX_ tx);
    croak("kx_server_session_keys: Failed to release ssk protmem RO");
  }

  if (protmem_release(aTHX_ rx, PROTMEM_FLAG_MPROTECT_RW) != 0
      || protmem_release(aTHX_ tx, PROTMEM_FLAG_MPROTECT_RW) != 0) {
    protmem_free(aTHX_ rx);
    protmem_free(aTHX_ tx);
    croak("kx_server_session_keys: Failed to release rx/tx protmem RW");
  }



( run in 0.663 second using v1.01-cache-2.11-cpan-71847e10f99 )