view release on metacpan or search on metacpan
inc/memvault.xs view on Meta::CPAN
}
++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;
lib/Crypt/Sodium/XS/MemVault.pm view on Meta::CPAN
die "randomly generated key matches loaded key: inconceivable!";
}
my $mv = mv_new("hello");
my $extracted_data_mv = $mv->extract(1, 3); # "ell"
print $extracted_data_mv->unlock, "\n";
undef $extracted_data_mv;
$mv->unlock;
my $mv2 = $mv->clone; # unlocked clone is unlocked
$mv2->xor_equals("\x{1f}\x{0a}\x{1e}\x{00}\x{0b}");
print "$mv, $mv2!\n";
my $colon_idx = $mv->index('ll'); # 2
my $unlocked_mv = $mv->clone;
$mv->lock;
=head1 DESCRIPTION
L<Crypt::Sodium::XS::MemVault> is the container for protected memory objects in
L<Crypt::Sodium::XS> which can be accessed from perl. It has constructors which
can read in the sensitive data without going through perl. It also provides
methods for manipulating the data while it remains only in protected memory.
These methods (except L</index>) are time-dependent only on the size of
protected data, not its content, so using them should not create any
lib/Crypt/Sodium/XS/MemVault.pm view on Meta::CPAN
Returns C<0> if the bytes are equal, C<-1> if C<$mv> is less than C<$other_mv>
(or C<$bytes>), or C<1> if C<$mv> is greater. This method runs in fixed-time
(for a given size), and compares bytes as little-endian arbitrary-length
integers. Comparible to the C<cmp> perl operator.
C<$size> is optional iif C<$mv> and C<$other_mv> (or C<$bytes>) are equal
sizes. If provided, only C<$size> bytes are compared. B<Note>: Croaks if a
comparison is of unequal sizes and C<$size> was not provided, or if C<$size> is
larger than either of the operands.
Croaks if C<$mv> or C<$other_mv> is conceptually "locked". See L</lock> and
L</unlock>.
B<Note>: This method is similar to L<memcmp(3)>; that is, it returns -1, 0, or
1 for the comparison results. For simple true/false equality comparisons, see
L</memcmp>. The naming is chosen here to be consistent with libsodium.
=head2 concat
my $new_mv = $mv->concat($bytes);
my $new_mv = $mv->concat($other_mv);
lib/Crypt/Sodium/XS/MemVault.pm view on Meta::CPAN
C<$offset>, or -1 if C<$substr> is not found.
C<$offset> is optional. If not provided, the search will begin at the start of
the protected memory. Unlike perl's index function, C<$offset> may not be
negative, and the method will croak if offset is beyond the last byte of
protected memory.
This method should only be used when protected memory starts with non-sensitive
data, and is guaranteed to find C<$substr> before any sensitive data.
Croaks if C<$mv> is conceptually "locked". See L</lock> and L</unlock>.
The C<Crypt::Sodium::XS::MemVault> object must be unlocked (L</unlock>) before
using this method.
=head2 is_locked
my $is_locked = $mv->is_locked;
Returns a boolean indicating if the object is conceptually "locked."
=head2 is_zero
my $is_null = $mv->is_zero;
Returns a boolean indicating if the protected memory consists of all null
bytes. Runs in constant time for a given size.
=head2 size
lib/Crypt/Sodium/XS/MemVault.pm view on Meta::CPAN
=head2 to_bytes
B<!!WARNING!!>: This method returns protected memory as a normal perl byte
string. This should not normally be necessary.
my $bytes = $mv->to_bytes;
Returns the protected memory as a byte string. This is the method used to
overload stringification. Consider carefully before using.
Croaks if C<$mv> is conceptually "locked". See L</lock> and L</unlock>.
=head2 to_hex
my $new_mv = $mv->to_hex;
Returns a new L<Crypt::Sodium::XS::MemVault> with the contents encoded as a
hexadecimal string.
=head2 unlock
lib/Crypt/Sodium/XS/MemVault.pm view on Meta::CPAN
my $less = $bytes < $mv;
my $less = $bytes lt $mv;
my $less = $mv < $other_mv;
my $less = $mv lt $other_mv;
...
All comparisons treat their operands as arbitrary-length little-endian
integers. Comparisons are made in fixed-type (for a given size), but the
results can leak information about protected memory. See L</compare>.
Croaks if C<$mv> or C<$other_mv> is conceptually "locked". See L</lock> and
L</unlock>.
=head2 stringification
my $var = "$mv";
Equivalent to L</to_bytes>. Stringification will croak if the
L<Crypt::Sodium::XS::MemVault> is conceptually "locked". See L</lock> and
L</unlock>. See also L</to_bytes>.
=head2 concatenation
my $new_mv = $mv . $string;
my $new_mv = $string . $mv;
my $new_mv = $mv . $other_mv;
Note: C<.=> is equivalent to L</concat_inplace>.
lib/Crypt/Sodium/XS/ProtMem.pm view on Meta::CPAN
The default flags for hash states are restrictive because the internal state
could be sensitive depending on use of the output. If no multipart hashing is
used in a way that internal state is sensitive, it is reasonable to set default
state flags to L</PROTMEM_ALL_DISABLED>.
The default flags for decrypted data are also restrictive. If decrypted data
will always be accessed from perl (in any way), then it may be reasonable
for your environment to also use L</PROTMEM_ALL_DISABLED> for decrypted data.
It might also be reasonable to use just L</PROTMEM_FLAGS_MLOCK_NONE> for
decrypted data if very large messages will be decrypted (at once rather than
chunked) to preserve limited locked memory. See the information below about
mlock.
=head2 CATEGORY-SPECIFIC RECOMMENDATIONS
mprotect protections should only be lessened when the perl interpreter and any
loaded libraries are fully trusted, including any threads (interpreter or
system) in use. It should probably not be disabled when perl is embedded in
another application; it offers increased security from bugs or vulnerabilities
in the application or its libraries. Consider carefully.
mlock protections should only be lessened when data isn't sensitive or on a
system with encrypted or no swap. If "Failed to allocate protmem" errors are
being generated because of limits on locked memory, it is a better idea to
increase the allowed locked memory (ulimit, prlimit) rather than removing the
protection. If that is not possible, follow the earlier advice before
disabling: no sensitive data, or no swap, or encrypted swap.
malloc protections should only be lessened if you're ok with using your
system's regular malloc, *as well as* disabling mprotect and mlock. It is
expected in the future to allow mprotect and mlock without libsodium's malloc.
memzero protection should really be left in place for any sensitive data.
Conceptual "locking" can be disabled if you expect to always extract the data
lib/Crypt/Sodium/XS/ProtMem.pm view on Meta::CPAN
Protected memory locking (preventing memory from swapping to disk) is allowed
to fail. This is libsodium's default behavior. Note that there is no flag to
entirely disable mlock of protected memory. This is a limitation of the
libsodium memory allocation interface.
=item PROTMEM_FLAGS_MLOCK_NONE
Although libsodium will always attempt to mlock memory when using its
allocator, by setting mlock flags to L</PROTMEM_MEMLOCK_NONE> the allocated
memory will be immediately munlocked. This can help preserve limited locked
memory resources.
=back
=back
=head2 MEMORY ALLOCATION (malloc) FLAGS
=over 4
lib/Crypt/Sodium/XS/ProtMem.pm view on Meta::CPAN
=item PROTMEM_MASK_LOCK
Bitwise mask of conceptual locking portion of flags. Bitwise and (C<&>) of this
mask and flags are equal to one of:
=over 4
=item PROTMEM_FLAGS_LOCK_LOCKED
MemVault will be locked by default, and must be unlocked to access the data.
=item PROTMEM_FLAGS_LOCK_UNLOCKED
MemVault will be unlocked by default, and may always be accessed.
=back
=back
=head2 ALL COMBINED
Two special flag sets have their own constants. They can be used directly as a
C<$flags> value without any masking necessary.
lib/Crypt/Sodium/XS/ProtMem.pm view on Meta::CPAN
unprotected during internal library operation when necessary. This guards
against any potential memory leaking bugs in perl itself or other parts of the
process runtime.
This protection can be lowered by setting flags to L</PROTMEM_FLAGS_MPROTECT_RO>
(read-only) or L</PROTMEM_FLAGS_MPROTECT_RW> (read-write, no protection). It is
disabled entirely by L</PROTMEM_FLAGS_MALLOC_PLAIN>.
=item mlock
By default, protected memory is locked with C<sodium_mlock>, which means that
it will not be swapped to disk. On operating systems supporting MAP_NOCORE or
MADV_DONTDUMP, it will also not be part of core dumps. Most operating systems
place limits on how much locked memory any individual process is allowed. On
such systems, C<ulimit> or C<prlimit> utility is likely available to increase
those limits if necessary.
This protection is disabled by any of the L</PROTMEM_FLAGS_MLOCK_PERMISSIVE>,
L</PROTMEM_FLAGS_MLOCK_NONE>, or L</PROTMEM_FLAGS_MALLOC_PLAIN> flags. B<NOTE>: When
using sodium malloc, libsodium always attempts to mlock allocated memory but
ignores failure. With L</PROTMEM_FLAGS_MLOCK_PERMISSIVE>, an extra mlock (to
ensure memory locking) call is avoided. With L</PROTMEM_FLAGS_MLOCK_NONE>, an
extra call is made to ensure memory is not locked.
=item libsodium malloc
By default, L<Crypt::Sodium::XS> protected memory objects are allocated with
C<sodium_malloc>, which is placed at the end of a page boundary, immediately
followed by a guard page. As a result, accessing memory past the end of the
region will immediately terminate the application. A canary is also placed
right before the returned pointer. Modifications of this canary are detected
when the object is no longer referenced and is freed with C<sodium_free>. This
will also cause the application to immediately terminate when detected. An
lib/Crypt/Sodium/XS/ProtMem.pm view on Meta::CPAN
There are performance gains in disabling the provided safety nets. It is still
strongly recommended to leave all the default protections in place. The feature
most likely to impact performance is the zeroing of memory when freed. Please
consider carefully before disabling!
=head2 MemVault
Sensitive data (secret keys and decrypted data) generated by
L<Crypt::Sodium::XS> is stored in a L<Crypt::Sodium::MemVault> object.
By default, all L<Crypt::Sodium::XS::MemVault> objects are in a "locked" state.
From perl code, this prevents direct access to the protected data; in order to
access it, it must first be "unlocked" with the C<unlock> method. It can be
re-locked after use with the C<lock> method. In general, a "locked" MemVault
will not allow data to be copied to perl-managed memory. Some methods (e.g.,
size, comparison, index) still provide potentially sensitive information about
contents of a "locked" MemVault. Great care should be taken directly using any
MemVault methods.
See L<Crypt::Sodium::XS::MemVault> for more.
=head2 multi-part states
All multi-part functionality for hashing, encryption, and decryption uses
protected memory objects as well, with the features listed above. Unlike
L<Crypt::Sodium::XS::MemVault>, there is no conceptual "locking" for these
objects, as the state data is not directly accessible to perl code.
t/memvault.t view on Meta::CPAN
my @mv_datas = (
"this is a string of things.",
"so is \x{ef}\x{bb}\x{bf} this one", # utf-8 encoded U+FFEF
);
for my $mv_data (@mv_datas) {
my $mv = Crypt::Sodium::XS::MemVault->new($mv_data);
isa_ok($mv, "Crypt::Sodium::XS::MemVault");
ok($mv->is_locked, "locked by default");
is($mv->size, length($mv_data), "correct length");
eval { my $x = "$mv"; };
like($@, qr/Unlock MemVault object before/, "cannot stringify locked bytes");
eval { my $x = $mv->to_bytes; };
like($@, qr/Unlock MemVault object before/, "cannot to_bytes locked bytes");
eval { my $x = $mv->index("dangerous"); };
like($@, qr/Unlock MemVault object before/, "cannot index locked bytes");
my $mv_clone = $mv->clone;
isa_ok($mv_clone, "Crypt::Sodium::XS::MemVault");
ok($mv_clone->is_locked, "clone of locked MemVault is_locked");
$mv->unlock;
ok(!$mv->is_locked, "unlocked MemVault !is_locked");
ok(!$mv->clone->is_locked, "clone of unlocked MemVault !is_locked");
like($mv->to_hex->unlock, qr/^[a-f0-9]+$/, "->to_hex format");
ok($mv->lock->is_locked, "locking returns the locked MemVault");
$mv = mv_new($mv_data);
isa_ok($mv, "Crypt::Sodium::XS::MemVault");
ok($mv->is_locked, "mv_new locked by default");
is($mv->size, length($mv_data), "mv_new correct length");
is($mv->unlock->to_bytes, $mv_data, "mv_new constructor correct bytes");
$mv = mv_from_hex(unpack("H*", $mv_data));
isa_ok($mv, "Crypt::Sodium::XS::MemVault");
ok($mv->is_locked, "new_from_hex locked by default");
is($mv->size, length($mv_data), "new_from_hex correct length");
is($mv->unlock->to_bytes, $mv_data, "new_from_hex correct bytes");
$mv = mv_from_base64(encode_base64($mv_data), BASE64_VARIANT_ORIGINAL);
isa_ok($mv, "Crypt::Sodium::XS::MemVault");
ok($mv->is_locked, "new_from_base64 locked by default");
is($mv->size, length($mv_data), "new_from_base64 correct length");
is($mv->unlock->to_bytes, $mv_data, "new_from_base64 correct bytes");
ok($mv eq $mv_data, "mv eq mv_data");
ok($mv == $mv_data, "mv == mv_data");
ok($mv_data eq $mv, "mv_data eq mv");
ok($mv_data == $mv, "mv_data == mv");
ok($mv eq $mv_clone, "mv eq clone");
ok($mv == $mv_clone, "mv == clone");
ok(!($mv ne $mv_clone), "! mv ne clone");
t/memvault.t view on Meta::CPAN
$mv_str = $mv->to_bytes;
ok($mv_str, "to_bytes works");
is(ref $mv_str, '', "to_bytes object is not a ref");
is($mv_str, $mv_data, "to_bytes object correct bytes");
is($mv->to_hex, unpack("H*", $mv_data), "->to_hex eq unpack");
ok($mv eq Crypt::Sodium::XS::MemVault->new_from_hex($mv->to_hex), "hex roundtripped");
is($mv->to_base64, encode_base64url($mv_data), "->to_base64 eq MIME::Base64");
ok($mv eq Crypt::Sodium::XS::MemVault->new_from_base64($mv->to_base64), "base64 roundtripped");
# should test on locked memvault as well, ensure result is locked
is($mv->extract(3), substr($mv_str, 3), "extract with +offset");
is($mv->extract(0, 3), substr($mv_str, 0, 3), "extract with +offset and +length");
is($mv->extract(-5), substr($mv_str, -5), "extract -offset");
is($mv->extract(-5, 3), substr($mv_str, -5, 3), "extract -offset and +length");
is($mv->extract(3, -3), substr($mv_str, 3, -3), "extract +offset and -length");
is($mv->extract(-5, -3), substr($mv_str, -5, -3), "extract -offset and -length");
is($mv->extract(0, $mv->size + 1), substr($mv_str, 0, $mv->size + 1),
"+length too big");
is($mv->extract(0, 0 - $mv->size - 1), substr($mv_str, 0, 0 - $mv->size - 1),
"-length too small");
t/memvault.t view on Meta::CPAN
is($mv->index("t"), index($mv_str, "t"), "index single char");
is($mv->index("this"), index($mv_str, "this"), "index word");
is($mv->index("t", 5), index($mv_str, "t", 5), "index single char with offset");
is($mv->index("thi", 9), index($mv_str, "thi", 9), "index word with offset");
is($mv->index("t", 999), index($mv_str, "t", 999), "index single char offset out of range");
$mv->lock;
my $mv_aaa = $mv . "aaa";
isa_ok($mv_aaa, "Crypt::Sodium::XS::MemVault");
ok($mv_aaa->is_locked, "overloaded .: MV . SV locked, result locked");
is($mv_aaa->to_hex->unlock, unpack("H*", $mv_data . "aaa"),
"overloaded .: MV . SV, correct result");
is($mv->to_hex->unlock, unpack("H*", $mv_data), "overloaded .: MV . SV did not mutate");
$mv_aaa = $mv->concat("aaa");
isa_ok($mv_aaa, "Crypt::Sodium::XS::MemVault");
ok($mv_aaa->is_locked, "concat: MV concat SV locked, result locked");
is($mv_aaa->to_hex->unlock, unpack("H*", $mv_data . "aaa"),
"concat: MV concat SV, correct result");
is($mv->to_hex->unlock, unpack("H*", $mv_data), "concat: did not mutate");
$mv_clone = $mv->clone;
$mv_clone .= "aaa";
ok($mv_clone->is_locked, "overloaded .=: MV .= SV locked, result locked");
is($mv_clone->to_hex->unlock, unpack("H*", $mv_data . "aaa"), "overloaded .=: correct results");
$mv_clone = $mv->clone;
$mv_clone->concat_inplace("aaa");
ok($mv_clone->is_locked, "concat_inplace: MV concat_inplace SV locked, result locked");
is($mv_clone->to_hex->unlock, unpack("H*", $mv_data . "aaa"), "concat_inplace: correct results");
my $aaa_key = "aaa" . $mv;
isa_ok($aaa_key, "Crypt::Sodium::XS::MemVault");
ok($aaa_key->is_locked, "overloaded .: SV . MV locked, result locked");
is($aaa_key->to_hex->unlock, unpack("H*", "aaa" . $mv_data),
"overloaded .: SV . MV, correct result");
is($mv->to_hex->unlock, unpack("H*", $mv_data), "overloaded .: SV . MV did not mutate");
$mv_aaa = $mv . Crypt::Sodium::XS::MemVault->new("aaa");
isa_ok($mv_aaa, "Crypt::Sodium::XS::MemVault");
ok($mv_aaa->is_locked, "overloaded .: MV . MV both locked, result locked");
is($mv_aaa->to_hex->unlock, unpack("H*", $mv_data . "aaa"),
"overloaded .: MV . MV both locked, correct result");
is($mv->to_hex->unlock, unpack("H*", $mv_data), "overloaded .: MV . MV did not mutate");
$mv_aaa = $mv . Crypt::Sodium::XS::MemVault->new("aaa")->unlock;
isa_ok($mv_aaa, "Crypt::Sodium::XS::MemVault");
ok($mv_aaa->is_locked, "overloaded .: MV . MV one locked, result locked");
is($mv_aaa->to_hex->unlock, unpack("H*", $mv_data . "aaa"),
"overloaded .: MV . MV one locked, correct result");
$mv_aaa = $mv->clone->unlock . Crypt::Sodium::XS::MemVault->new("aaa")->unlock;
isa_ok($mv_aaa, "Crypt::Sodium::XS::MemVault");
ok(!$mv_aaa->is_locked, "overloaded .: MV . MV none locked, result unlocked");
is($mv_aaa->to_hex, unpack("H*", $mv_data . "aaa"),
"overloaded .: MV . MV none locked, correct result");
my $mv_x_5 = $mv x 5;
isa_ok($mv_x_5, "Crypt::Sodium::XS::MemVault");
ok($mv_x_5->is_locked, "overloaded x: locked input, locked output");
$mv_x_5 = $mv->unlock x 5;
ok(!$mv_x_5->is_locked, "overloaded x: locked input, locked output");
is($mv_x_5->to_hex,
unpack("H*", "${mv_data}${mv_data}${mv_data}${mv_data}${mv_data}"), "mv x 5");
}
{ # compare
my $x = Crypt::Sodium::XS::MemVault->new("abc")->unlock;
my $y = "abC";
my $z = Crypt::Sodium::XS::MemVault->new("abC")->unlock;
t/memvault.t view on Meta::CPAN
ok($x <= "zbc", "overloaded <=: SV to lesser MV is true");
ok(!($x > "zbc"), "overloaded >: SV to lesser MV is false");
ok(!($x >= "zbc"), "overloaded >=: SV to lesser MV is false");
ok("abd" > $x, "overloaded >: MV to lesser SV is true");
ok("abd" >= $x, "overloaded >=: MV to lesser SV is true");
ok(!("abd" < $x), "overloaded <: MV to lesser SV is false");
ok(!("abd" <= $x), "overloaded <=: MV to lesser SV is false");
$y->lock;
eval { $y->compare("abc") };
like($@, qr/Unlock MemVault object before/, "cannot compare locked mv invocant");
eval { $z->compare($y); };
like($@, qr/Unlock MemVault object before/, "cannot compare locked mv arg");
eval { my $junk = $y > "abc" };
like($@, qr/Unlock MemVault object before/, "overloaded >: cannot compare locked mv");
eval { my $junk = "abc" > $y };
like($@, qr/Unlock MemVault object before/, "overloaded >: cannot compare locked mv swap");
eval { my $junk = $z > $y };
like($@, qr/Unlock MemVault object before/, "overloaded >: cannot compare locked mv to mv");
eval { my $junk = $y > $z };
like($@, qr/Unlock MemVault object before/, "overloaded >: cannot compare locked mv to mv swap");
$y = Crypt::Sodium::XS::MemVault->new("abcdefghi")->unlock;
$z = Crypt::Sodium::XS::MemVault->new("zbcdef")->unlock;
ok($x->memcmp($y, 2), "memcmp: MV to MV with length are equal");
ok(!$x->compare($y, 2), "compare: MV to MV with length are equal");
ok(!$x->memcmp($z, 2), "memcmp: MV to different MV with length differ");
ok($x->compare($z, 2), "compare: MV to different MV with length differ");
eval { my $res = $x->memcmp("abcde"); };
like($@, qr/Variables of unequal size/,
t/memvault.t view on Meta::CPAN
is($mv->to_hex, "010203", "xor_equals method with memvault arg");
undef $mv2;
my $secret = "secret secrets are no fun...";
my $tmpfile = File::Temp->new;
print $tmpfile $secret;
$tmpfile->flush;
$mv = Crypt::Sodium::XS::MemVault->new_from_file($tmpfile->filename);
isa_ok($mv, "Crypt::Sodium::XS::MemVault");
ok($mv->is_locked, "new_from_file locked by default");
is($mv->size, length($secret), "new_from_file correct length");
is($mv->unlock, $secret, "new_from_file correct data");
$mv = mv_from_file($tmpfile->filename);
isa_ok($mv, "Crypt::Sodium::XS::MemVault");
is($mv->unlock, $secret, "mv_from_file correct data");
$mv = Crypt::Sodium::XS::MemVault->new_from_file($tmpfile->filename, 16);
ok($mv->is_locked, "MemVault from file, 16 bytes, locked by default");
is($mv->size, 16, "MemVault from file, 16 bytes, correct length");
is($mv->unlock, substr($secret, 0, 16), "MemVault from file, 16 bytes, correct data");
$tmpfile->seek(0, 0);
$mv = Crypt::Sodium::XS::MemVault->new_from_fd(fileno($tmpfile));
is($mv->unlock, $secret, "new_from_fd correct data");
$tmpfile->seek(0, 0);
$mv = mv_from_fd(fileno($tmpfile));
is($mv->unlock, $secret, "mv_from_fd correct data");
$tmpfile->seek(0, 0);
ok($pk, "pk generated with random seed ($alg)");
ok($sk, "sk generated with random seed ($alg)");
my $signed = $m->sign($msg, $sk);
ok($signed, "signed msg ($alg)");
my $msg_mv = Crypt::Sodium::XS::MemVault->new($msg);
my $signed_mv = $m->sign($msg_mv, $sk);
ok($signed_mv, "signed MemVault msg ($alg)");
isa_ok($signed_mv, "Crypt::Sodium::XS::MemVault");
ok($signed_mv->is_locked, "signing locked MemVault returns locked");
my $opened = $m->open($signed, $pk);
ok($opened, "opened signed msg ($alg)");
is($opened, $msg, "opened msg matches original ($alg)");
my $opened_mv = $m->open($signed_mv, $pk);
ok($opened_mv, "opened MemVault signed msg ($alg)");
isa_ok($opened_mv, "Crypt::Sodium::XS::MemVault");
ok($opened_mv->is_locked, "opening locked MemVault returns locked");
is($opened_mv->unlock, $msg, "opened MemVault msg matches original ($alg)");
# detached mode
my $sig = $m->detached($msg, $sk);
ok($sig, "signature calculated for msg using sk ($alg)");
ok($m->verify($msg, $sig, $pk),
"signed message verified using sig ($alg)");
ok(!$m->verify("Some other message.", $sig, $pk),
"wrong message fails to verify using sig ($alg)");