Crypt-JWT
view release on metacpan or search on metacpan
lib/Crypt/KeyWrap.pm view on Meta::CPAN
package Crypt::KeyWrap;
use strict;
use warnings;
our $VERSION = '0.038';
use Exporter 'import';
our %EXPORT_TAGS = ( all => [qw(aes_key_wrap aes_key_unwrap gcm_key_wrap gcm_key_unwrap pbes2_key_wrap pbes2_key_unwrap ecdh_key_wrap ecdh_key_unwrap ecdhaes_key_wrap ecdhaes_key_unwrap rsa_key_wrap rsa_key_unwrap)] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
use Carp;
use Crypt::Mode::ECB;
use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
use Crypt::PRNG qw(random_bytes);
use Crypt::KeyDerivation qw(pbkdf2);
use Crypt::Digest qw(digest_data);
use Crypt::Misc qw(decode_b64u encode_b64u);
use Config;
# JWS: https://tools.ietf.org/html/rfc7515
# JWE: https://tools.ietf.org/html/rfc7516
# JWK: https://tools.ietf.org/html/rfc7517
# JWA: https://tools.ietf.org/html/rfc7518 - !!! this is important !!!
sub _LSB {
my ($bytes, $data) = @_;
my $len = length $data;
return $len > $bytes ? substr($data, $len-$bytes, $bytes) : $data;
}
sub _MSB {
my ($bytes, $data) = @_;
my $len = length $data;
return $len > $bytes ? substr($data, 0, $bytes) : $data;
}
sub _N2RAW {
my ($bytes, $n) = @_;
if ($bytes == 8) {
return pack("N", 0) . pack("N", $n) if $Config{uvsize} == 4; #workaround
return pack("N", $n >> 32) . pack("N", $n & 0xFFFFFFFF);
}
return pack("N", $n & 0xFFFFFFFF) if $bytes == 4;
}
sub _decode_ecdh_info {
my ($name, $value) = @_;
return '' unless defined $value;
croak "concat_kdf: invalid $name" if ref $value;
my $decoded = decode_b64u($value);
croak "concat_kdf: invalid $name" unless defined $decoded && encode_b64u($decoded) eq $value;
return $decoded;
}
sub aes_key_wrap {
my ($kek, $pt_data, $cipher, $padding, $inverse) = @_;
$cipher = 'AES' unless defined $cipher;
$padding = $cipher eq 'AES' ? 1 : 0 unless defined $padding;
my ($A, $B, $P, $R);
croak "aes_key_wrap: no KEK" unless defined $kek;
croak "aes_key_wrap: no PT data" unless defined $pt_data;
my $klen = length $kek;
croak "aes_key_wrap: invalid KEK length" unless $klen == 16 || $klen == 24 || $klen == 32;
croak "aes_key_wrap: cipher must be AES or DES_EDE" unless $cipher eq 'AES' || $cipher eq 'DES_EDE';
croak "aes_key_wrap: padding not allowed with DES_EDE" if $padding && $cipher eq 'DES_EDE';
my $ECB = Crypt::Mode::ECB->new($cipher, 0);
my $blck = $cipher eq 'DES_EDE' ? 4 : 8; # semiblock size in bytes, for AES 8, for 3DES 4
# IV selection per RFC 3394 (KW, no padding) vs RFC 5649 (KWP, with padding).
# Strict semantics: KWP always uses the alternate IV (A65959A6 || msg-len),
# even when the message is already aligned, and KW always uses the standard
# all-A6 IV. This matches the strict-mode behaviour Wycheproof tests for.
my $len = length $pt_data;
my $IV;
if ($padding) {
croak "aes_key_wrap: KWP only defined for AES (blck=8)" if $blck != 8;
$IV = pack("H*", "A65959A6") . pack("N", $len);
if ($len % $blck > 0) {
$pt_data .= chr(0) x ($blck - ($len % $blck));
}
}
else {
croak "aes_key_wrap: pt_data length not multiply of $blck" if $len % $blck != 0;
$IV = pack("H*", "A6" x $blck);
}
my $n = length($pt_data) / $blck;
$P->[$_] = substr($pt_data, $_*$blck, $blck) for (0..$n-1);
if ($n == 1) {
return $inverse ? $ECB->decrypt($IV . $P->[0], $kek)
: $ECB->encrypt($IV . $P->[0], $kek);
}
$A = $IV;
$R->[$_] = $P->[$_] for (0..$n-1);
for my $j (0..5) {
for my $i (0..$n-1) {
$B = $inverse ? $ECB->decrypt($A . $R->[$i], $kek)
: $ECB->encrypt($A . $R->[$i], $kek);
$A = _MSB($blck, $B) ^ _N2RAW($blck, ($n*$j)+$i+1);
$R->[$i] = _LSB($blck, $B);
}
}
my $rv = $A;
$rv .= $R->[$_] for (0..$n-1);
return $rv;
( run in 0.464 second using v1.01-cache-2.11-cpan-99c4e6809bf )