1package Crypt::KeyWrap;
2
3use strict;
4use warnings;
5
6our $VERSION = '0.034';
7
8use Exporter 'import';
9our %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)] );
10our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
11our @EXPORT = qw();
12
13use Carp;
14use Crypt::Mode::ECB;
15use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
16use Crypt::PRNG qw(random_bytes);
17use Crypt::KeyDerivation qw(pbkdf2);
18use Crypt::Digest qw(digest_data);
19use Config;
20
21# JWS: https://tools.ietf.org/html/rfc7515
22# JWE: https://tools.ietf.org/html/rfc7516
23# JWK: https://tools.ietf.org/html/rfc7517
24# JWA: https://tools.ietf.org/html/rfc7518 - !!! this is important !!!
25
26sub _LSB {
27  my ($bytes, $data) = @_;
28  my $len = length $data;
29  return $len > $bytes ? substr($data, $len-$bytes, $bytes) : $data;
30}
31
32sub _MSB {
33  my ($bytes, $data) = @_;
34  my $len = length $data;
35  return $len > $bytes ? substr($data, 0, $bytes) : $data;
36}
37
38sub _N2RAW {
39  my ($bytes, $n) = @_;
40  if ($bytes == 8) {
41    return pack("N", 0) . pack("N", $n) if $Config{uvsize} == 4; #workaround
42    return pack("N", $n >> 32) . pack("N", $n & 0xFFFFFFFF);
43  }
44  return pack("N", $n & 0xFFFFFFFF) if $bytes == 4;
45}
46
47sub aes_key_wrap {
48  my ($kek, $pt_data, $cipher, $padding, $inverse) = @_;
49  $cipher  = 'AES' unless defined $cipher;
50  $padding = $cipher eq 'AES' ? 1 : 0 unless defined $padding;
51
52  my ($A, $B, $P, $R);
53
54  croak "aes_key_wrap: no KEK"     unless defined $kek;
55  croak "aes_key_wrap: no PT data" unless defined $pt_data;
56  my $klen = length $kek;
57  croak "aes_key_wrap: invalid KEK length" unless $klen == 16 || $klen == 24 || $klen == 32;
58  croak "aes_key_wrap: cipher must be AES or DES_EDE" unless $cipher eq 'AES' || $cipher eq 'DES_EDE';
59  croak "aes_key_wrap: padding not allowed with DES_EDE" if $padding && $cipher eq 'DES_EDE';
60
61  my $ECB = Crypt::Mode::ECB->new($cipher, 0);
62  my $blck = $cipher eq 'DES_EDE' ? 4 : 8; # semiblock size in bytes, for AES 8, for 3DES 4
63
64  my $IV = pack("H*", "A6" x $blck);
65  my $len = length $pt_data;
66  if ($len % $blck > 0) {
67    croak "aes_key_wrap: pt_data length not multiply of $blck" if !$padding;
68    $pt_data .= chr(0) x ($blck - ($len % $blck));
69    $IV = pack("H*", "A65959A6") . pack("N", $len);
70  }
71
72  my $n = length($pt_data) / $blck;
73  $P->[$_] = substr($pt_data, $_*$blck, $blck) for (0..$n-1);
74
75  if ($n == 1) {
76    return $inverse ? $ECB->decrypt($IV . $P->[0], $kek)
77                    : $ECB->encrypt($IV . $P->[0], $kek);
78  }
79
80  $A = $IV;
81  $R->[$_] = $P->[$_] for (0..$n-1);
82
83  for my $j (0..5) {
84    for my $i (0..$n-1) {
85      $B = $inverse ? $ECB->decrypt($A . $R->[$i], $kek)
86                    : $ECB->encrypt($A . $R->[$i], $kek);
87      $A = _MSB($blck, $B) ^ _N2RAW($blck, ($n*$j)+$i+1);
88      $R->[$i] = _LSB($blck, $B);
89    }
90  }
91
92  my $rv = $A;
93  $rv .= $R->[$_] for (0..$n-1);
94  return $rv;
95}
96
97sub aes_key_unwrap {
98  my ($kek, $ct_data, $cipher, $padding, $inverse) = @_;
99  $cipher  = 'AES' unless defined $cipher;
100  $padding = $cipher eq 'AES' ? 1 : 0 unless defined $padding;
101
102  my ($A, $B, $C, $P, $R);
103
104  croak "aes_key_unwrap: no KEK"     unless defined $kek;
105  croak "aes_key_unwrap: no CT data" unless defined $ct_data;
106  my $klen = length $kek;
107  croak "aes_key_unwrap: invalid KEK length" unless $klen == 16 || $klen == 24 || $klen == 32;
108  croak "aes_key_unwrap: cipher must be AES or DES_EDE" unless $cipher eq 'AES' || $cipher eq 'DES_EDE';
109  croak "aes_key_unwrap: padding not allowed with DES_EDE" if $padding && $cipher eq 'DES_EDE';
110
111  my $ECB = Crypt::Mode::ECB->new($cipher, 0);
112  my $blck = $cipher eq 'DES_EDE' ? 4 : 8; # semiblock size in bytes, for AES 8, for 3DES 4
113
114  my $n = length($ct_data) / $blck - 1;
115  $C->[$_] = substr($ct_data, $_*$blck, $blck) for (0..$n); # n+1 semiblocks
116
117  if ($n==1) {
118    $B = $inverse ? $ECB->encrypt($C->[0] . $C->[1], $kek)
119                  : $ECB->decrypt($C->[0] . $C->[1], $kek);
120    $A = _MSB($blck, $B);
121    $R->[0] = _LSB($blck, $B);
122  }
123  else {
124    $A = $C->[0];
125    $R->[$_] = $C->[$_+1] for (0..$n-1);
126    for(my $j=5; $j>=0; $j--) {
127      for(my $i=$n-1; $i>=0; $i--) {
128        $B = $inverse ? $ECB->encrypt(($A ^ _N2RAW($blck, $n*$j+$i+1)) . $R->[$i], $kek)
129                      : $ECB->decrypt(($A ^ _N2RAW($blck, $n*$j+$i+1)) . $R->[$i], $kek);
130        $A = _MSB($blck, $B);
131        $R->[$i] = _LSB($blck, $B);
132      }
133    }
134  }
135
136  my $rv = '';
137  $rv .= $R->[$_] for (0..$n-1);
138
139  my $A_hex = unpack("H*", $A);
140  if ($A_hex eq 'a6'x$blck) {
141    return $rv;
142  }
143  elsif ($A_hex =~ /^a65959a6/ && $blck == 8) {
144    warn "key_unwrap: unexpected padding" unless $padding;
145    my $n = unpack("N", substr($A, 4, 4));
146    my $z = length($rv) - $n;
147    my $tail = unpack("H*", substr($rv, -$z));
148    croak "aes_key_unwrap: invalid data" unless $tail eq "00"x$z;
149    return substr($rv, 0, $n);
150  }
151  croak "aes_key_unwrap: unexpected data [$cipher/$A_hex]";
152}
153
154# AES GCM KW - https://tools.ietf.org/html/rfc7518#section-4.7
155
156sub gcm_key_wrap {
157  my ($kek, $pt_data, $aad, $cipher, $iv) = @_;
158  $cipher = 'AES' unless defined $cipher;
159  $iv = random_bytes(12) unless defined $iv; # 96 bits REQUIRED by RFC7518
160  my ($ct_data, $tag) = gcm_encrypt_authenticate($cipher, $kek, $iv, $aad, $pt_data);
161  return ($ct_data, $tag, $iv);
162}
163
164sub gcm_key_unwrap {
165  my ($kek, $ct_data, $tag, $iv, $aad, $cipher) = @_;
166  $cipher ||= 'AES';
167  my $pt_data = gcm_decrypt_verify($cipher, $kek, $iv, $aad, $ct_data, $tag);
168  return $pt_data;
169}
170
171# PBES2/PBKDF2 KW - https://tools.ietf.org/html/rfc7518#section-4.8
172
173sub pbes2_key_wrap {
174  my ($kek, $pt_data, $alg, $salt, $iter) = @_;
175  my ($hash_name, $len);
176  if ($alg =~ /^PBES2-HS(256|384|512)\+A(128|192|256)KW$/) {
177    $hash_name = "SHA$1";
178    $len = $2/8;
179    my $aes_key = pbkdf2($kek, $alg."\x00".$salt, $iter, $hash_name, $len);
180    my $ct_data = aes_key_wrap($aes_key, $pt_data);
181    return $ct_data;
182  }
183  croak "pbes2_key_wrap: invalid alg '$alg'";
184  return undef;
185}
186
187sub pbes2_key_unwrap {
188  my ($kek, $ct_data, $alg, $salt, $iter) = @_;
189  my ($hash_name, $len);
190  if ($alg =~ /^PBES2-HS(256|384|512)\+A(128|192|256)KW$/) {
191    $hash_name = "SHA$1";
192    $len = $2/8;
193    my $aes_key = pbkdf2($kek, $alg."\x00".$salt, $iter, $hash_name, $len);
194    my $pt_data = aes_key_unwrap($aes_key, $ct_data);
195    return $pt_data;
196  }
197  croak "pbes2_key_unwrap: invalid alg '$alg'";
198  return undef;
199}
200
201# RSA KW
202# https://tools.ietf.org/html/rfc7518#section-4.2
203# https://tools.ietf.org/html/rfc7518#section-4.3
204
205sub rsa_key_wrap {
206  my ($kek_public, $pt_data, $alg) = @_;
207  croak "rsa_key_wrap: no Crypt::PK::RSA" unless ref $kek_public eq 'Crypt::PK::RSA';
208  my ($padding, $hash_name);
209  if    ($alg eq 'RSA-OAEP')     { ($padding, $hash_name) = ('oaep', 'SHA1') }
210  elsif ($alg eq 'RSA-OAEP-256') { ($padding, $hash_name) = ('oaep', 'SHA256') }
211  elsif ($alg eq 'RSA1_5')       { $padding = 'v1.5' }
212  croak "rsa_key_wrap: invalid algorithm '$alg'" unless $padding;
213  my $ct_data = $kek_public->encrypt($pt_data, $padding, $hash_name);
214  return $ct_data;
215}
216
217sub rsa_key_unwrap {
218  my ($kek_private, $ct_data, $alg) = @_;
219  croak "rsa_key_unwrap: no Crypt::PK::RSA" unless ref $kek_private eq 'Crypt::PK::RSA';
220  croak "rsa_key_unwrap: no private key" unless $kek_private->is_private;
221  my ($padding, $hash_name);
222  if    ($alg eq 'RSA-OAEP')     { ($padding, $hash_name) = ('oaep', 'SHA1') }
223  elsif ($alg eq 'RSA-OAEP-256') { ($padding, $hash_name) = ('oaep', 'SHA256') }
224  elsif ($alg eq 'RSA1_5')       { $padding = 'v1.5' }
225  croak "rsa_key_unwrap: invalid algorithm '$alg'" unless $padding;
226  my $pt_data = $kek_private->decrypt($ct_data, $padding, $hash_name);
227  return $pt_data;
228}
229
230# ConcatKDF - http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf
231# ECDH KW   - https://tools.ietf.org/html/rfc7518#section-4.6
232
233sub _concat_kdf {
234  my ($hash_name, $key_size, $shared_secret, $algorithm, $apu, $apv) = @_;
235  $apu = '' unless defined $apu;
236  $apv = '' unless defined $apv;
237  my $hsize = Crypt::Digest->hashsize($hash_name);
238  my $count = int($key_size / $hsize);
239  $count++ if ($key_size % $hsize) > 0;
240  my $data = '';
241  for my $i (1..$count) {
242    $data .= digest_data('SHA256', pack("N", 1) .
243                                   $shared_secret .
244                                   pack("N", length($algorithm)) . $algorithm .
245                                   pack("N", length($apu)) . $apu .
246                                   pack("N", length($apv)) . $apv .
247                                   pack("N", 8 *$key_size));
248  }
249  return substr($data, 0, $key_size);
250}
251
252sub ecdh_key_wrap {
253  my ($kek_public, $enc, $apu, $apv) = @_;
254  croak "ecdh_key_wrap: no Crypt::PK::ECC" unless ref $kek_public eq 'Crypt::PK::ECC';
255  my $encryption_key_size = 256;
256  if ($enc =~ /^A(128|192|256)CBC-HS/) {
257    $encryption_key_size = $1*2;
258  }
259  if ($enc =~ /^A(128|192|256)GCM/) {
260    $encryption_key_size = $1;
261  }
262  my $ephemeral = Crypt::PK::ECC->new()->generate_key($kek_public->curve2hash);
263  my $shared_secret = $ephemeral->shared_secret($kek_public);
264  my $ct_data = _concat_kdf('SHA256', $encryption_key_size/8, $shared_secret, $enc, $apu, $apv);
265  return ($ct_data, $ephemeral->export_key_jwk('public'));
266}
267
268sub ecdh_key_unwrap {
269  my ($kek_private, $enc, $epk, $apu, $apv) = @_;
270  croak "ecdh_key_unwrap: no Crypt::PK::ECC" unless ref $kek_private eq 'Crypt::PK::ECC';
271  croak "ecdh_key_unwrap: no private key" unless $kek_private->is_private;
272  my $encryption_key_size = 256;
273  if ($enc =~ /^A(128|192|256)CBC-HS/) {
274    $encryption_key_size = $1*2;
275  }
276  if ($enc =~ /^A(128|192|256)GCM/) {
277    $encryption_key_size = $1;
278  }
279  my $ephemeral = ref($epk) eq 'Crypt::PK::ECC' ? $epk : Crypt::PK::ECC->new(ref $epk ? $epk : \$epk);
280  my $shared_secret = $kek_private->shared_secret($ephemeral);
281  my $pt_data = _concat_kdf('SHA256', $encryption_key_size/8, $shared_secret, $enc, $apu, $apv);
282  return $pt_data;
283}
284
285sub ecdhaes_key_wrap {
286  my ($kek_public, $pt_data, $alg, $apu, $apv) = @_;
287  croak "ecdhaes_key_wrap: no Crypt::PK::(ECC|X25519)" unless ref($kek_public) =~ /^Crypt::PK::(ECC|X25519)$/;
288  my $encryption_key_size = 256;
289  if ($alg =~ /^ECDH-ES\+A(128|192|256)KW$/) {
290    $encryption_key_size = $1;
291  }
292  my $ephemeral;
293  if (ref($kek_public) eq 'Crypt::PK::ECC') {
294    $ephemeral = Crypt::PK::ECC->new->generate_key($kek_public->curve2hash);
295  }
296  else {
297    $ephemeral = Crypt::PK::X25519->new->generate_key();
298  }
299  my $shared_secret = $ephemeral->shared_secret($kek_public);
300  my $kek = _concat_kdf('SHA256', $encryption_key_size/8, $shared_secret, $alg, $apu, $apv);
301  return (aes_key_wrap($kek, $pt_data), $ephemeral->export_key_jwk('public'));
302}
303
304sub ecdhaes_key_unwrap {
305  my ($kek_private, $ct_data, $alg, $epk, $apu, $apv) = @_;
306  croak "ecdhaes_key_unwrap: no Crypt::PK::(ECC|X25519)" unless ref($kek_private) =~ /^Crypt::PK::(ECC|X25519)$/;
307  croak "ecdhaes_key_unwrap: no private key" unless $kek_private->is_private;
308  my $encryption_key_size = 256;
309  if ($alg =~ /^ECDH-ES\+A(128|192|256)KW$/) {
310    $encryption_key_size = $1;
311  }
312  my $ephemeral;
313  if (ref($kek_private) eq 'Crypt::PK::ECC') {
314    $ephemeral = ref($epk) eq 'Crypt::PK::ECC' ? $epk : Crypt::PK::ECC->new(ref $epk ? $epk : \$epk);
315  }
316  else {
317    $ephemeral = ref($epk) eq 'Crypt::PK::X25519' ? $epk : Crypt::PK::X25519->new(ref $epk ? $epk : \$epk);
318  }
319  my $shared_secret = $kek_private->shared_secret($ephemeral);
320  my $kek = _concat_kdf('SHA256', $encryption_key_size/8, $shared_secret, $alg, $apu, $apv);
321  my $pt_data = aes_key_unwrap($kek, $ct_data);
322  return $pt_data;
323}
324
3251;
326
327=pod
328
329=head1 NAME
330
331Crypt::KeyWrap - Key management/wrapping algorithms defined in RFC7518 (JWA)
332
333=head1 SYNOPSIS
334
335   # A192KW wrapping
336   use Crypt::KeyWrap qw(aes_key_wrap);
337   my $kek     = pack("H*", "5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8"); # key encryption key
338   my $cek     = pack("H*", "c37b7e6492584340bed12207808941155068f738"); # content encryption key
339   my $enc_cek = aes_key_wrap($kek, $pt_data); # encrypted content encryption key
340
341   # A192KW unwrapping
342   use Crypt::KeyWrap qw(aes_key_unwrap);
343   my $kek     = pack("H*", "5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8");
344   my $enc_cek = pack("H*", "138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a");
345   my $cek     = aes_key_unwrap($kek, $pt_data);
346
347=head1 DESCRIPTION
348
349Implements key management algorithms defined in L<https://tools.ietf.org/html/rfc7518>
350
351BEWARE: experimental, interface of this module might change!
352
353Supported algorithms (all defined in RFC7518):
354
355 A128KW                 see: aes_key_wrap() + aes_key_unwrap()
356 A192KW                 see: aes_key_wrap() + aes_key_unwrap()
357 A256KW                 see: aes_key_wrap() + aes_key_unwrap()
358 A128GCMKW              see: gcm_key_wrap() + gcm_key_unwrap()
359 A192GCMKW              see: gcm_key_wrap() + gcm_key_unwrap()
360 A256GCMKW              see: gcm_key_wrap() + gcm_key_unwrap()
361 PBES2-HS256+A128KW     see: pbes2_key_wrap() + pbes2_key_unwrap()
362 PBES2-HS384+A192KW     see: pbes2_key_wrap() + pbes2_key_unwrap()
363 PBES2-HS512+A256KW     see: pbes2_key_wrap() + pbes2_key_unwrap()
364 RSA-OAEP               see: rsa_key_wrap() + rsa_key_unwrap()
365 RSA-OAEP-256           see: rsa_key_wrap() + rsa_key_unwrap()
366 RSA1_5                 see: rsa_key_wrap() + rsa_key_unwrap()
367 ECDH-ES+A128KW         see: ecdhaes_key_wrap() + ecdhaes_key_unwrap()
368 ECDH-ES+A192KW         see: ecdhaes_key_wrap() + ecdhaes_key_unwrap()
369 ECDH-ES+A256KW         see: ecdhaes_key_wrap() + ecdhaes_key_unwrap()
370 ECDH-ES                see: ecdh_key_wrap() + ecdh_key_unwrap()
371
372=head1 EXPORT
373
374Nothing is exported by default.
375
376You can export selected functions:
377
378  use Crypt::KeyWrap qw(aes_key_wrap gcm_key_wrap pbes2_key_wrap);
379
380Or all of them at once:
381
382  use Crypt::KeyWrap ':all';
383
384=head1 FUNCTIONS
385
386=head2 aes_key_wrap
387
388AES key wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.4>
389(implements algorithms C<A128KW>, C<A192KW>, C<A256KW>).
390
391Implementation follows L<https://tools.ietf.org/html/rfc5649> and L<https://tools.ietf.org/html/rfc3394>.
392
393The implementation is also compatible with L<http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf>
394(it supports AES based KW, KWP + TDEA/DES_EDE based TKW).
395
396AES Key Wrap algorithm.
397
398   $enc_cek = aes_key_wrap($kek, $cek);
399   # or
400   $enc_cek = aes_key_wrap($kek, $cek, $cipher, $padding, $inverse);
401
402   # params:
403   #  $kek     .. key encryption key (16bytes for AES128, 24 for AES192, 32 for AES256)
404   #  $cek     .. content encryption key
405   # optional params:
406   #  $cipher  .. 'AES' (default) or 'DES_EDE'
407   #  $padding .. 1 (default) or 0 handle $cek padding (relevant for AES only)
408   #  $inverse .. 0 (default) or 1 use cipher in inverse mode as defined by SP.800-38F
409
410Values C<$enc_cek>, C<$cek> and C<$kek> are binary octets. If you disable padding you have to make sure that
411C<$cek> length is multiply of 8 (for AES) or multiply of 4 (for DES_EDE);
412
413=head2 aes_key_unwrap
414
415AES key unwrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.4>
416(implements algorithms C<A128KW>, C<A192KW>, C<A256KW>).
417
418AES Key Unwrap algorithm.
419
420   $cek = aes_key_unwrap($kek, $enc_cek);
421   # or
422   $cek = aes_key_unwrap($kek, $enc_cek, $cipher, $padding, $inverse);
423
424   # params:
425   #  $kek     .. key encryption key (16bytes for AES128, 24 for AES192, 32 for AES256)
426   #  $enc_cek .. encrypted content encryption key
427   # optional params:
428   #  $cipher  .. 'AES' (default) or 'DES_EDE'
429   #  $padding .. 1 (default) or 0 - use $cek padding (relevant for AES only)
430   #  $inverse .. 0 (default) or 1 - use cipher in inverse mode as defined by SP.800-38F
431
432Values C<$enc_cek>, C<$cek> and C<$kek> are binary octets.
433
434=head2 gcm_key_wrap
435
436AES GCM key wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.7>
437(implements algorithms C<A128GCMKW>, C<A192GCMKW>, C<A256GCMKW>).
438
439   ($enc_cek, $tag, $iv) = gcm_key_wrap($kek, $cek);
440   #or
441   ($enc_cek, $tag, $iv) = gcm_key_wrap($kek, $cek, $aad);
442   #or
443   ($enc_cek, $tag, $iv) = gcm_key_wrap($kek, $cek, $aad, $cipher, $iv);
444
445   # params:
446   #  $kek     .. key encryption key (16bytes for AES128, 24 for AES192, 32 for AES256)
447   #  $cek     .. content encryption key
448   # optional params:
449   #  $aad     .. additional authenticated data, DEFAULT is '' (empty string)
450   #  $cipher  .. cipher to be used by GCM, DEFAULT is 'AES'
451   #  $iv      .. initialization vector (if not defined a random IV is generated)
452
453Values C<$enc_cek>, C<$cek>, C<$aad>, C<$iv>, C<$tag> and C<$kek> are binary octets.
454
455=head2 gcm_key_unwrap
456
457AES GCM key unwrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.7>
458(implements algorithms C<A128GCMKW>, C<A192GCMKW>, C<A256GCMKW>).
459
460   $cek = gcm_key_unwrap($kek, $enc_cek, $tag, $iv);
461   # or
462   $cek = gcm_key_unwrap($kek, $enc_cek, $tag, $iv, $aad);
463   # or
464   $cek = gcm_key_unwrap($kek, $enc_cek, $tag, $iv, $aad, $cipher);
465
466   # params:
467   #  $kek     .. key encryption key (16bytes for AES128, 24 for AES192, 32 for AES256)
468   #  $enc_cek .. encrypted content encryption key
469   #  $tag     .. GCM's tag
470   #  $iv      .. initialization vector
471   # optional params:
472   #  $aad     .. additional authenticated data, DEFAULT is '' (empty string)
473   #  $cipher  .. cipher to be used by GCM, DEFAULT is 'AES'
474
475Values C<$enc_cek>, C<$cek>, C<$aad>, C<$iv>, C<$tag> and C<$kek> are binary octets.
476
477=head2 pbes2_key_wrap
478
479PBES2 key wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.8>
480(implements algorithms C<PBES2-HS256+A128KW>, C<PBES2-HS384+A192KW>, C<PBES2-HS512+A256KW>).
481
482   $enc_cek = pbes2_key_wrap($kek, $cek, $alg, $salt, $iter);
483
484   # params:
485   #  $kek     .. key encryption key (arbitrary length)
486   #  $cek     .. content encryption key
487   #  $alg     .. algorithm name e.g. 'PBES2-HS256+A128KW' (see rfc7518)
488   #  $salt    .. pbkdf2 salt
489   #  $iter    .. pbkdf2 iteration count
490
491Values C<$enc_cek>, C<$cek>, C<$salt> and C<$kek> are binary octets.
492
493=head2 pbes2_key_unwrap
494
495PBES2 key unwrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.8>
496(implements algorithms C<PBES2-HS256+A128KW>, C<PBES2-HS384+A192KW>, C<PBES2-HS512+A256KW>).
497
498   $cek = pbes2_key_unwrap($kek, $enc_cek, $alg, $salt, $iter);
499
500   # params:
501   #  $kek     .. key encryption key (arbitrary length)
502   #  $enc_cek .. encrypted content encryption key
503   #  $alg     .. algorithm name e.g. 'PBES2-HS256+A128KW' (see rfc7518)
504   #  $salt    .. pbkdf2 salt
505   #  $iter    .. pbkdf2 iteration count
506
507Values C<$enc_cek>, C<$cek>, C<$salt> and C<$kek> are binary octets.
508
509=head2 rsa_key_wrap
510
511PBES2 key wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.2> and
512L<https://tools.ietf.org/html/rfc7518#section-4.3> (implements algorithms C<RSA1_5>, C<RSA-OAEP-256>, C<RSA-OAEP>).
513
514   $enc_cek = rsa_key_wrap($kek, $cek, $alg);
515
516   # params:
517   #  $kek     .. RSA public key - Crypt::PK::RSA instance
518   #  $cek     .. content encryption key
519   #  $alg     .. algorithm name e.g. 'RSA-OAEP' (see rfc7518)
520
521Values C<$enc_cek> and C<$cek> are binary octets.
522
523=head2 rsa_key_unwrap
524
525PBES2 key wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.2> and
526L<https://tools.ietf.org/html/rfc7518#section-4.3> (implements algorithms C<RSA1_5>, C<RSA-OAEP-256>, C<RSA-OAEP>).
527
528   $cek = rsa_key_unwrap($kek, $enc_cek, $alg);
529
530   # params:
531   #  $kek     .. RSA private key - Crypt::PK::RSA instance
532   #  $enc_cek .. encrypted content encryption key
533   #  $alg     .. algorithm name e.g. 'RSA-OAEP' (see rfc7518)
534
535Values C<$enc_cek> and C<$cek> are binary octets.
536
537=head2 ecdhaes_key_wrap
538
539ECDH+AESKW key agreement/wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.6>
540(implements algorithms C<ECDH-ES+A128KW>, C<ECDH-ES+A192KW>, C<ECDH-ES+A256KW>).
541
542   ($enc_cek, $epk) = ecdhaes_key_wrap($kek, $cek, $alg, $apu, $apv);
543
544   # params:
545   #  $kek     .. ECC public key - Crypt::PK::ECC|X25519 instance
546   #  $cek     .. content encryption key
547   #  $alg     .. algorithm name e.g. 'ECDH-ES+A256KW' (see rfc7518)
548   # optional params:
549   #  $apu     .. Agreement PartyUInfo Header Parameter
550   #  $apv     .. Agreement PartyVInfo Header Parameter
551
552Values C<$enc_cek> and C<$cek> are binary octets.
553
554=head2 ecdhaes_key_unwrap
555
556ECDH+AESKW key agreement/unwrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.6>
557(implements algorithms C<ECDH-ES+A128KW>, C<ECDH-ES+A192KW>, C<ECDH-ES+A256KW>).
558
559   $cek = ecdhaes_key_unwrap($kek, $enc_cek, $alg, $epk, $apu, $apv);
560
561   # params:
562   #  $kek     .. ECC private key - Crypt::PK::ECC|X25519 instance
563   #  $enc_cek .. encrypted content encryption key
564   #  $alg     .. algorithm name e.g. 'ECDH-ES+A256KW' (see rfc7518)
565   #  $epk     .. ephemeral ECC public key (JWK/JSON or Crypt::PK::ECC|X25519)
566   # optional params:
567   #  $apu     .. Agreement PartyUInfo Header Parameter
568   #  $apv     .. Agreement PartyVInfo Header Parameter
569
570Values C<$enc_cek> and C<$cek> are binary octets.
571
572=head2 ecdh_key_wrap
573
574ECDH (Ephememeral Static) key agreement/wrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.6>
575(implements algorithm C<ECDH-ES>).
576
577   ($cek, $epk) = ecdh_key_wrap($kek, $enc, $apu, $apv);
578
579   # params:
580   #  $kek     .. ECC public key - Crypt::PK::ECC|X25519 instance
581   #  $enc     .. encryption algorithm name e.g. 'A256GCM' (see rfc7518)
582   # optional params:
583   #  $apu     .. Agreement PartyUInfo Header Parameter
584   #  $apv     .. Agreement PartyVInfo Header Parameter
585
586Value C<$cek> - binary octets, C<$epk> JWK/JSON string with ephemeral ECC public key.
587
588=head2 ecdh_key_unwrap
589
590ECDH (Ephememeral Static) key agreement/unwrap algorithm as defined in L<https://tools.ietf.org/html/rfc7518#section-4.6>
591(implements algorithm C<ECDH-ES>).
592
593   $cek = ecdh_key_unwrap($kek, $enc, $epk, $apu, $apv);
594
595   # params:
596   #  $kek     .. ECC private key - Crypt::PK::ECC|X25519 instance
597   #  $enc     .. encryption algorithm name e.g. 'A256GCM' (see rfc7518)
598   #  $epk     .. ephemeral ECC public key (JWK/JSON or Crypt::PK::ECC|X25519)
599   # optional params:
600   #  $apu     .. Agreement PartyUInfo Header Parameter
601   #  $apv     .. Agreement PartyVInfo Header Parameter
602
603Value C<$cek> - binary octets.
604
605=head1 SEE ALSO
606
607L<Crypt::Cipher::AES>, L<Crypt::AuthEnc::GCM>, L<Crypt::PK::RSA>, L<Crypt::KeyDerivation>
608
609=head1 LICENSE
610
611This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
612
613=head1 COPYRIGHT
614
615Copyright (c) 2015-2021 DCIT, a.s. L<https://www.dcit.cz> / Karel Miko
616