Crypt-Sodium-XS

 view release on metacpan or  search on metacpan

inc/memvault.xs  view on Meta::CPAN


  tattr.c_lflag |= ICANON | ISIG;
  tattr.c_lflag &= ~ECHO;
  if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) {
    write(fd, "\n", 1);
    protmem_free(aTHX_ new_pm);
    croak("new_from_ttyno: Failed tcsetattr");
  }
  tattr.c_lflag = lflag_prev; /* prepared for next tcsetattr to reset */

  for (;;) {
    r = read(fd, new_pm->pm_ptr, 4096);
    if (r < 0) {
      if (errno == EINTR)
        continue;
      write(fd, "\n", 1);
      protmem_free(aTHX_ new_pm);
      tcsetattr(fd, TCSAFLUSH, &tattr);
      croak("new_from_ttyno: Failed read");
    }
    else
      break;
  }
  if (r == 0)
    write(fd, " (WARNING: empty)", (sizeof " (WARNING: empty)" - 1));
  else if (*((unsigned char *)new_pm->pm_ptr + r - 1) == '\n')
    r -= 1;
  else if (*((unsigned char *)new_pm->pm_ptr + r - 1) == '\r')
    r -= 1;
  else if (r == 4096)
    write(fd, " (WARNING: truncated to 4096 bytes)",
          (sizeof " (WARNING: truncated to 4096 bytes)" - 1));

  new_pm->size = r;

  write(fd, "\n", 1);
  if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("new_from_ttyno: tcsetattr failed");
  }

  RETVAL = protmem_to_sv(aTHX_ new_pm, class);

  OUTPUT:
  RETVAL

void DESTROY(SV * self)

  PREINIT:
  protmem *self_pm;

  CODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  protmem_free(aTHX_ self_pm);

void _overload_bool(SV * self, ...)

  PREINIT:
  protmem *self_pm;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);

  if (self_pm->size)
    XSRETURN_YES;
  else
    XSRETURN_NO;

SV * _overload_mult(SV * self, SV * other, SV * swapped)

  PREINIT:
  protmem *self_pm;
  protmem *new_pm;
  unsigned int count = 0;
  unsigned int cur = 0;

  CODE:
  PERL_UNUSED_VAR(swapped);

  count = SvUV(other);

  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  new_pm = protmem_init(aTHX_ self_pm->size * count, self_pm->flags);
  if (new_pm == NULL)
    croak("_overload_mult: Failed to allocate protmem");

  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("_overload_mult: Failed to grant self protmem RO");

  while(count--)
    memcpy(new_pm->pm_ptr + self_pm->size * cur++, self_pm->pm_ptr, self_pm->size);

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("_overload_mult: Failed to release self protmem RO");
  }

  if (protmem_release(aTHX_ new_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("_overload_mult: Failed to release new protmem RW");
  }

  RETVAL = protmem_to_sv(aTHX_ new_pm, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

void _overload_nomethod(SV * self, ...)

  PREINIT:
  char *operator;

  PPCODE:
  PERL_UNUSED_VAR(self);
  operator = SvPVbyte_nolen(ST(3));
  croak("Operation \"%s\" on MemVault is not supported", operator);

void bitwise_and(SV * self, SV * other, ...)

  ALIAS:
  bitwise_or = 1
  bitwise_xor = 2
  bitwise_and_equals = 100
  bitwise_or_equals = 101
  bitwise_xor_equals = 102

  PREINIT:
  protmem *self_pm;
  protmem *other_pm = NULL;
  protmem *new_pm = NULL;
  unsigned char *buf;
  unsigned char *other_buf;
  STRLEN other_len;
  STRLEN i;
  unsigned int new_flags;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);

  if (sv_derived_from(other, MEMVAULT_CLASS)) {
    other_pm = protmem_get(aTHX_ other, MEMVAULT_CLASS);
    other_buf = other_pm->pm_ptr;
    other_len = other_pm->size;
  }
  else
    other_buf = (unsigned char *)SvPVbyte(other, other_len);
  if (other_len != self_pm->size)
    /* lengths MUST be identical below */
    croak("Exclusive-or operands do not have equal length");

  if (ix >= 100) {
    if (other_pm)
      if (protmem_grant(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
        protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
        croak("xor: Failed to grant other protmem RO");
      }
    if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      croak("xor: Failed to grant self protmem RW");
    }
    buf = self_pm->pm_ptr;
    /* lengths identical, assured above */
    for (i = 0; i < other_len; i++)
      switch(ix) {
        case 101:
          buf[i] |= other_buf[i];
          break;
        case 102:
          buf[i] ^= other_buf[i];
          break;
        default: /* 100 */
          buf[i] &= other_buf[i];
      }
    if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      croak("xor: Failed to release self protmem RW");
    }
  }
  else {
    new_flags = self_pm->flags;
    if (other_pm)
      new_flags &= other_pm->flags;
    new_pm = protmem_init(aTHX_ self_pm->size, new_flags);
    if (new_pm == NULL)
      croak("xor: Failed to allocate protmem");

    if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
      croak("xor: Failed to grant self protmem RO");

    if (other_pm)
      if (protmem_grant(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
        protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
        croak("xor: Failed to grant other protmem RO");
      }

    buf = memcpy(new_pm->pm_ptr, self_pm->pm_ptr, self_pm->size);
    for (i = 0; i < other_len; i++)
      switch(ix) {
        case 1:
          buf[i] |= other_buf[i];
          break;
        case 2:
          buf[i] ^= other_buf[i];
          break;
        default:
          buf[i] &= other_buf[i];
      }

    if (protmem_release(aTHX_ new_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
      protmem_free(aTHX_ new_pm);
      croak("xor: Failed to release new protmem RW");
    }
    if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      protmem_free(aTHX_ new_pm);
      croak("xor: Failed to release self protmem RW");
    }

    mXPUSHs(protmem_to_sv(aTHX_ new_pm, MEMVAULT_CLASS));
  }

  if (other_pm)
    if (protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
      croak("xor: Failed to release other protmem RO");

  XSRETURN(1);

SV * clone(SV * self)

  CODE:
  RETVAL = protmem_clone_sv(aTHX_ self, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

void compare(SV * self, SV * other, STRLEN size = 0)

  ALIAS:
  _overload_eq = 1
  _overload_ne = 2
  memcmp = 3

  PREINIT:
  protmem *self_pm = NULL, *other_pm = NULL;
  unsigned char *self_buf, *other_buf;
  STRLEN self_size, other_size;
  int ret = 0;

  PPCODE:
  /* since used for overloads, args could be swapped. could require either self
   * or other to be a memvault */
  if (sv_derived_from(self, MEMVAULT_CLASS)) {
    self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
    if (ix == 0 && !(self_pm->flags & PROTMEM_FLAG_LOCK_UNLOCKED))
      croak("compare: Unlock MemVault object before comparison");
    self_buf = self_pm->pm_ptr;
    self_size = self_pm->size;
  }
  else
    self_buf = (unsigned char *)SvPVbyte(self, self_size);

  if (sv_derived_from(other, MEMVAULT_CLASS)) {
    other_pm = protmem_get(aTHX_ other, MEMVAULT_CLASS);
    if (ix == 0 && !(other_pm->flags & PROTMEM_FLAG_LOCK_UNLOCKED))
      croak("compare: Unlock MemVault object before comparison");
    other_buf = other_pm->pm_ptr;
    other_size = other_pm->size;
  }
  else
    other_buf = (unsigned char *)SvPVbyte(other, other_size);

  if (self_size != other_size) {
    switch(ix) {
      case 1: /* fallthrough */
      case 2:
        croak("compare: %s %s",
              "Variables of unequal size cannot be automatically compared.",
              "Please use memcmp() with the size argument provided.");
        break;
      default:
        if (size == 0) {
          croak("compare: %s %s",
                "Variables of unequal size cannot be automatically compared.",
                "Please provide the size argument.");
        }
        else {
          if (size > self_size)
            croak("compare: The argument (left) is shorter then requested size");
          else if (size > other_size)
            croak("compare: The argument (right) is shorter then requested size");
        }
    }
  }

  if (self_pm)
    if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
      croak("compare: Failed to grant self protmem RO");

  if (other_pm) {
    if (protmem_grant(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
      if (self_pm)
        protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
      croak("compare: Failed to grant other protmem RO");
    }
  }

  if (ix != 0)
    ret = sodium_memcmp(self_buf, other_buf, self_size);
  else
    ret = sodium_compare(self_buf, other_buf, self_size);

  if (other_pm && protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    if (self_pm)
      protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
    croak("compare: Failed to release other protmem RO");
  }
  if (self_pm && protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("compare: Failed to release self protmem RO");

  if (ix == 0) {
    XSRETURN_IV(ret);
  }
  else if (ix == 2) {
    if (ret == 0)
      XSRETURN_NO;
    XSRETURN_YES;
  }
  else {
    if (ret == 0)
      XSRETURN_YES;
    XSRETURN_NO;
  }

=for TODO

consider a flags argument. that cannot co-exist with overloading as it is now.

=cut

void concat(SV * self, SV * other, SV * swapped = &PL_sv_undef)

  ALIAS:
  concat_inplace = 1

  PREINIT:
  protmem *self_pm;
  protmem *other_pm = NULL;
  protmem *new_pm;
  unsigned char *buf;
  MAGIC *mg, *mg_found=NULL;
  STRLEN buf_len;
  unsigned int new_flags;

  PPCODE:
  if (sv_derived_from(other, MEMVAULT_CLASS)) {
    other_pm = protmem_get(aTHX_ other, MEMVAULT_CLASS);
    buf = other_pm->pm_ptr;
    buf_len = other_pm->size;
  }
  else
    buf = (unsigned char *)SvPVbyte(other, buf_len);
    /* should probably zero buf afterwards */

  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  new_flags = self_pm->flags;
  if (other_pm)
    new_flags &= other_pm->flags;
  new_pm = protmem_init(aTHX_ self_pm->size + buf_len, new_flags);
  if (new_pm == NULL)
    croak("concat: Failed to allocate protmem");

  if (other_pm && protmem_grant(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("concat: Failed to grant other protmem RO");
  }

  if (ix == 0) {
    if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      protmem_free(aTHX_ new_pm);
      croak("concat: Failed to grant self protmem RO");
    }

    if (SvTRUE(swapped))
      memcpy(memcpy(new_pm->pm_ptr, buf, buf_len) + buf_len,
             self_pm->pm_ptr, self_pm->size);
    else
      memcpy(memcpy(new_pm->pm_ptr, self_pm->pm_ptr, self_pm->size)
             + self_pm->size, buf, buf_len);

    if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      protmem_free(aTHX_ new_pm);
      croak("concat: Failed to release self protmem RO");
    }

    if (protmem_release(aTHX_ new_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      protmem_free(aTHX_ new_pm);
      croak("concat: Failed to release new protmem RW");
    }

    mXPUSHs(protmem_to_sv(aTHX_ new_pm, MEMVAULT_CLASS));
  }
  else {
    if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
      if (other_pm)
        protmem_release(aTHX_ other_pm, PROTMEM_FLAG_MPROTECT_RO);
      croak("concat: Failed to grant self protmem RW");
    }

inc/memvault.xs  view on Meta::CPAN

    protmem_free(aTHX_ new_pm);
    croak("from_base64: Failed to release new protmem RW");
  }

  RETVAL = protmem_to_sv(aTHX_ new_pm, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

SV * from_hex(SV * self, SV * flags = &PL_sv_undef)

  PREINIT:
  protmem *self_pm;
  protmem *new_pm;
  char *self_buf = NULL;
  STRLEN self_len;
  unsigned int new_flags = g_protmem_default_flags_memvault;

  CODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  self_buf = (char *)self_pm->pm_ptr;
  self_len = self_pm->size;
  new_flags = self_pm->flags;

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

  new_pm = protmem_init(aTHX_ self_len / 2, new_flags);
  if (new_pm == NULL)
    croak("from_hex: Failed to allocate protmem");

  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("from_hex: Failed to grant self protmem RO");
  }

  sodium_hex2bin(new_pm->pm_ptr, new_pm->size, self_buf, self_len,
                NULL, NULL, NULL);

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("from_hex: Failed to release self protmem RO");
  }

  if (protmem_release(aTHX_ new_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("from_hex: Failed to release new protmem RW");
  }

  RETVAL = protmem_to_sv(aTHX_ new_pm, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

void increment(SV * self)

  PREINIT:
  protmem *self_pm;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) != 0)
    croak("increment: Failed to grant self protmem RW");

  sodium_increment(self_pm->pm_ptr, self_pm->size);

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) != 0)
    croak("increment: Failed to release self protmem RW");

  XSRETURN(1);

SV * index(SV * self, SV * str, STRLEN offset = 0)

  PREINIT:
  protmem *self_pm;
  char *str_buf;
  unsigned char *self_start, *self_p, *self_stop;
  STRLEN str_len;

  CODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);

  if (!(self_pm->flags & PROTMEM_FLAG_LOCK_UNLOCKED))
    croak("index: Unlock MemVault object before using index");

  if (offset > self_pm->size - 1)
    XSRETURN_IV(-1);
  str_buf = SvPVbyte(str, str_len);
  if (str_len < 1)
    XSRETURN_IV(0);
  self_start = (unsigned char *)self_pm->pm_ptr;
  self_p = self_start + offset;
  self_stop = self_start + self_pm->size - str_len; /* + 1 - 1 */
  if (self_p >= self_stop)
    XSRETURN_IV(-1);

  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("index: Failed to grant self protmem RO");

  RETVAL = &PL_sv_undef;

  /* naive implementation, good nuff for "unsafe" */
  while (self_p <= self_stop) {
    if (*self_p == str_buf[0]) {
      if (str_len == 1) {
        RETVAL=newSVuv(self_p - self_start);
        break;
      }
      else {
        if (memcmp(self_p, str_buf, str_len) == 0) {
          RETVAL = newSVuv(self_p - self_start);
          break;
        }
      }
    }
    ++self_p;
  }

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("index: Failed to release self protmem RO");

  OUTPUT:
  RETVAL

void is_locked(SV * self)

  PREINIT:
  protmem *self_pm;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);

  if (self_pm->flags & PROTMEM_FLAG_LOCK_UNLOCKED)
    XSRETURN_NO;

  XSRETURN_YES;

void is_zero(SV * self)

  PREINIT:
  protmem *self_pm;
  int ret;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("is_zero: Failed to grant self protmem RO");

  ret = sodium_is_zero(self_pm->pm_ptr, self_pm->size);

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("is_zero: Failed to release self protmem RO");

  if (ret)
    XSRETURN_YES;

  XSRETURN_NO;

SV * length(SV * self)

  ALIAS:
  size = 1

  PREINIT:
  protmem *self_pm;

  CODE:
  PERL_UNUSED_VAR(ix);
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  RETVAL = newSVuv((UV)self_pm->size);

  OUTPUT:
  RETVAL

void lock(SV * self)

  PREINIT:
  protmem *self_pm;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  self_pm->flags &= ~PROTMEM_FLAG_LOCK_UNLOCKED;
  XSRETURN(1);

SV * pad(SV * self, STRLEN blocksize)

  PREINIT:
  protmem *self_pm, *realloc_pm;
  STRLEN buf_len, pad_len, padded_len;

  CODE:
  if (blocksize <= 0)
    croak("pad: Invalid blocksize <= 0");

  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  buf_len = self_pm->size;

  pad_len = blocksize - 1;
  if ((blocksize & (blocksize - 1)) == 0)
    pad_len -= buf_len & (blocksize - 1);
  else
    pad_len -= buf_len % blocksize;
  pad_len += 1; /* for 0x80 */

  if ((STRLEN)SIZE_MAX - buf_len <= pad_len)
    croak("pad: Pad exceeds SIZE_MAX");
  padded_len = buf_len + pad_len;

  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("pad: Failed to grant self protmem RO");

  realloc_pm = protmem_clone(aTHX_ self_pm, padded_len);
  if (realloc_pm == NULL) {
    protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
    croak("pad: Failed to allocate protmem");
  }

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ realloc_pm);
    croak("pad: Failed to release self protmem RO");
  }

  if (sodium_pad(&padded_len, realloc_pm->pm_ptr,
                 buf_len, blocksize, padded_len) != 0) {
    /* should be impossible */
    protmem_free(aTHX_ realloc_pm);
    croak("BUG: pad: sodium_pad returned error");
  }

  if (protmem_release(aTHX_ realloc_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
    protmem_free(aTHX_ realloc_pm);
    croak("pad: Failed to release protmem RW");
  }

  RETVAL = protmem_to_sv(aTHX_ realloc_pm, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

SV * to_base64( \

inc/memvault.xs  view on Meta::CPAN

    protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
    protmem_free(aTHX_ new_pm);
    croak("to_hex: Failed to release new protmem RW");
  }

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ new_pm);
    croak("to_hex: Failed to release self protmem RO");
  }

  RETVAL = protmem_to_sv(aTHX_ new_pm, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

SV * to_fd(SV * self, int fd)

  PREINIT:
  protmem *self_pm;
  ssize_t w;
  size_t t = 0;
  size_t r = 0;

  CODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) < 0)
    croak("to_fd: Failed to grant self protmem RO");

  /* w: current iteration written bytes */
  /* t: total bytes written */
  /* r: remaining bytes to write */
  r = self_pm->size;
  for (;;) {
    w = write(fd, self_pm->pm_ptr + t,
              r > MEMVAULT_WRITE_BUFSIZE ? MEMVAULT_WRITE_BUFSIZE : r);
    if (w < 0) {
      if (errno == EINTR)
        continue;
      protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
      croak("to_fd: (%d): write error", fd);
    }
    t += w;
    r -= w;
    if (r == 0)
      break;
  }

  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) < 0)
    croak("to_fd: Failed to release self protmem RO");

  RETVAL = newSVuv(t);

  OUTPUT:
  RETVAL

void unlock(SV * self)

  PREINIT:
  protmem *self_pm;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  self_pm->flags |= PROTMEM_FLAG_LOCK_UNLOCKED;
  XSRETURN(1);

SV * unpad(SV * self, STRLEN blocksize)

  PREINIT:
  protmem *self_pm, *realloc_pm;
  STRLEN buf_len, unpadded_len;

  CODE:
  if (blocksize <= 0)
    croak("unpad: Invalid blocksize <= 0");

  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  buf_len = self_pm->size;
  if (buf_len < blocksize)
    croak("unpad: Buffer is shorter than blocksize");

  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0)
    croak("unpad: Failed to grant self protmem RO");

  if (sodium_unpad(&unpadded_len, self_pm->pm_ptr, self_pm->size, blocksize) != 0) {
    protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
    croak("unpad: Invalid padded buffer");
  }

  realloc_pm = protmem_clone(aTHX_ self_pm, unpadded_len);
  if (realloc_pm == NULL) {
    protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
    croak("sodium_pad: Failed to allocate protmem");
  }

  if (protmem_release(aTHX_ realloc_pm, PROTMEM_FLAG_MPROTECT_RW) != 0) {
    protmem_free(aTHX_ realloc_pm);
    protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO);
    croak("sodium_pad: Failed to release protmem RW");
  }
  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RO) != 0) {
    protmem_free(aTHX_ realloc_pm);
    croak("sodium_pad: Failed to release self protmem RO");
  }

  RETVAL = protmem_to_sv(aTHX_ realloc_pm, MEMVAULT_CLASS);

  OUTPUT:
  RETVAL

void memzero(SV * self)

  PREINIT:
  protmem *self_pm;

  PPCODE:
  self_pm = protmem_get(aTHX_ self, MEMVAULT_CLASS);
  if (protmem_grant(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) < 0)
    croak("memzero: Failed to grant self protmem RW");
  sodium_memzero(self_pm->pm_ptr, self_pm->size);
  if (protmem_release(aTHX_ self_pm, PROTMEM_FLAG_MPROTECT_RW) < 0)
    croak("memzero: Failed to release self protmem RW");

=for FIXME

  separate methods for xor (modify in place) from the overload (new
  memvault). currently no explicit method for returning new memvault, only the
  overload.

=cut



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