1From https://github.com/openbsd/ports/blob/master/net/p5-Net-SNMP/patches/patch-lib_Net_SNMP_Security_USM_pm
2Tests for the additional algorithms have also been added.
3
4diff --git a/MANIFEST b/MANIFEST
5index 3430564..d3dd7f0 100644
6--- a/MANIFEST
7+++ b/MANIFEST
8@@ -40,3 +40,7 @@ t/usm-sha1-3des.t
9 t/usm-sha1-aes.t
10 t/usm-sha1-cfb192aes.t
11 t/usm-sha1-des.t
12+t/usm-sha224-aes.t
13+t/usm-sha256-aes.t
14+t/usm-sha384-aes.t
15+t/usm-sha512-aes.t
16diff --git a/lib/Net/SNMP/Security/USM.pm b/lib/Net/SNMP/Security/USM.pm
17index a76ef56..0bcd52c 100644
18--- a/lib/Net/SNMP/Security/USM.pm
19+++ b/lib/Net/SNMP/Security/USM.pm
20@@ -26,8 +26,9 @@ use Net::SNMP::Message qw(
21 use Crypt::DES();
22 use Digest::MD5();
23 use Digest::SHA();
24-use Digest::HMAC_MD5();
25-use Digest::HMAC_SHA1();
26+
27+use Digest::SHA qw( hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512 );
28+use Digest::HMAC_MD5 qw ( hmac_md5 );
29
30 ## Version of the Net::SNMP::Security::USM module
31
32@@ -41,7 +42,9 @@ our @EXPORT_OK;
33
34 our %EXPORT_TAGS = (
35 authprotos => [
36- qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA )
37+ qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA
38+ AUTH_PROTOCOL_HMACSHA224 AUTH_PROTOCOL_HMACSHA256
39+ AUTH_PROTOCOL_HMACSHA384 AUTH_PROTOCOL_HMACSHA512 )
40 ],
41 levels => [
42 qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
43@@ -64,9 +67,13 @@ $EXPORT_TAGS{ALL} = [ @EXPORT_OK ];
44
45 ## RCC 3414 - Authentication protocols
46
47-sub AUTH_PROTOCOL_NONE { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
48-sub AUTH_PROTOCOL_HMACMD5 { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
49-sub AUTH_PROTOCOL_HMACSHA { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
50+sub AUTH_PROTOCOL_NONE { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
51+sub AUTH_PROTOCOL_HMACMD5 { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
52+sub AUTH_PROTOCOL_HMACSHA { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
53+sub AUTH_PROTOCOL_HMACSHA224 { '1.3.6.1.6.3.10.1.1.4' } # usmHMAC128SHA224AuthProtocol
54+sub AUTH_PROTOCOL_HMACSHA256 { '1.3.6.1.6.3.10.1.1.5' } # usmHMAC192SHA256AuthProtocol
55+sub AUTH_PROTOCOL_HMACSHA384 { '1.3.6.1.6.3.10.1.1.6' } # usmHMAC256SHA384AuthProtocol
56+sub AUTH_PROTOCOL_HMACSHA512 { '1.3.6.1.6.3.10.1.1.7' } # usmHMAC384SHA512AuthProtocol
57
58 ## RFC 3414 - Privacy protocols
59
60@@ -125,6 +132,7 @@ sub new
61 '_time_epoc' => time(), # snmpEngineBoots epoc
62 '_user_name' => q{}, # securityName
63 '_auth_data' => undef, # Authentication data
64+ '_auth_maclen' => undef, # MAC length
65 '_auth_key' => undef, # authKey
66 '_auth_password' => undef, # Authentication password
67 '_auth_protocol' => AUTH_PROTOCOL_HMACMD5, # authProtocol
68@@ -281,10 +289,10 @@ sub generate_request_msg
69 if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
70
71 # Save the location to fill in msgAuthenticationParameters later
72- $auth_location = $msg->length() + 12 + length $pdu_buffer;
73+ $auth_location = $msg->length() + $this->{_auth_maclen} + length $pdu_buffer;
74
75 # Set the msgAuthenticationParameters to all zeros
76- $auth_params = pack 'x12';
77+ $auth_params = pack "x$this->{_auth_maclen}";
78 }
79
80 if (!defined $msg->prepare(OCTET_STRING, $auth_params)) {
81@@ -419,12 +427,12 @@ sub process_incoming_msg
82 # to compute the HMAC properly.
83
84 if (my $len = length $auth_params) {
85- if ($len != 12) {
86+ if ($len != $this->{_auth_maclen}) {
87 return $this->_error(
88 'The msgAuthenticationParameters length of %d is invalid', $len
89 );
90 }
91- substr ${$msg->reference}, ($msg->index() - 12), 12, pack 'x12';
92+ substr ${$msg->reference}, ($msg->index() - $this->{_auth_maclen}), $this->{_auth_maclen}, pack "x$this->{_auth_maclen}";
93 }
94
95 # msgPrivacyParameters::=OCTET STRING
96@@ -748,6 +756,18 @@ sub _auth_password
97 quotemeta AUTH_PROTOCOL_HMACMD5, AUTH_PROTOCOL_HMACMD5,
98 '(?:hmac-)?sha(?:-?1|-96)?', AUTH_PROTOCOL_HMACSHA,
99 quotemeta AUTH_PROTOCOL_HMACSHA, AUTH_PROTOCOL_HMACSHA,
100+ '(?:hmac-)?sha(?:-?224)', AUTH_PROTOCOL_HMACSHA224,
101+ 'usmHMAC128SHA224AuthProtocol', AUTH_PROTOCOL_HMACSHA224,
102+ quotemeta AUTH_PROTOCOL_HMACSHA224,AUTH_PROTOCOL_HMACSHA224,
103+ '(?:hmac-)?sha(?:-?256)', AUTH_PROTOCOL_HMACSHA256,
104+ 'usmHMAC192SHA256AuthProtocol', AUTH_PROTOCOL_HMACSHA256,
105+ quotemeta AUTH_PROTOCOL_HMACSHA256,AUTH_PROTOCOL_HMACSHA256,
106+ '(?:hmac-)?sha(?:-?384)', AUTH_PROTOCOL_HMACSHA384,
107+ 'usmHMAC256SHA384AuthProtocol', AUTH_PROTOCOL_HMACSHA384,
108+ quotemeta AUTH_PROTOCOL_HMACSHA384,AUTH_PROTOCOL_HMACSHA384,
109+ '(?:hmac-)?sha(?:-?512)', AUTH_PROTOCOL_HMACSHA512,
110+ 'usmHMAC384SHA512AuthProtocol', AUTH_PROTOCOL_HMACSHA512,
111+ quotemeta AUTH_PROTOCOL_HMACSHA512,AUTH_PROTOCOL_HMACSHA512,
112 };
113
114 sub _auth_protocol
115@@ -1100,7 +1120,7 @@ sub _authenticate_outgoing_msg
116 }
117
118 # Set the msgAuthenticationParameters
119- substr ${$msg->reference}, -$auth_location, 12, $this->_auth_hmac($msg);
120+ substr ${$msg->reference}, -$auth_location, $this->{_auth_maclen}, $this->_auth_hmac($msg);
121
122 return TRUE;
123 }
124@@ -1126,7 +1146,7 @@ sub _auth_hmac
125 return q{} if (!defined($this->{_auth_data}) || !defined $msg);
126
127 return substr
128- $this->{_auth_data}->reset()->add(${$msg->reference()})->digest(), 0, 12;
129+ $this->{_auth_data}(${$msg->reference()}, $this->{_auth_key}), 0, $this->{_auth_maclen};
130 }
131
132 sub _auth_data_init
133@@ -1141,16 +1161,35 @@ sub _auth_data_init
134
135 if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
136
137- $this->{_auth_data} =
138- Digest::HMAC_MD5->new($this->{_auth_key});
139+ $this->{_auth_data} = \&hmac_md5;
140+ $this->{_auth_maclen} = 12;
141
142 } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
143
144- $this->{_auth_data} =
145- Digest::HMAC_SHA1->new($this->{_auth_key});
146+ $this->{_auth_data} = \&hmac_sha1;
147+ $this->{_auth_maclen} = 12;
148+
149+ } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA224) {
150+
151+ $this->{_auth_data} = \&hmac_sha224;
152+ $this->{_auth_maclen} = 16;
153+
154+ } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA256) {
155+
156+ $this->{_auth_data} = \&hmac_sha256;
157+ $this->{_auth_maclen} = 24;
158+
159+ } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA384) {
160+
161+ $this->{_auth_data} = \&hmac_sha384;
162+ $this->{_auth_maclen} = 32;
163+
164+ } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA512) {
165+
166+ $this->{_auth_data} = \&hmac_sha512;
167+ $this->{_auth_maclen} = 48;
168
169 } else {
170-
171 return $this->_error(
172 'The authProtocol "%s" is unknown', $this->{_auth_protocol}
173 );
174@@ -1628,6 +1667,10 @@ sub _auth_key_validate
175 {
176 AUTH_PROTOCOL_HMACMD5, [ 16, 'HMAC-MD5' ],
177 AUTH_PROTOCOL_HMACSHA, [ 20, 'HMAC-SHA1' ],
178+ AUTH_PROTOCOL_HMACSHA224, [ 28, 'HMAC-SHA224' ],
179+ AUTH_PROTOCOL_HMACSHA256, [ 32, 'HMAC-SHA256' ],
180+ AUTH_PROTOCOL_HMACSHA384, [ 48, 'HMAC-SHA384' ],
181+ AUTH_PROTOCOL_HMACSHA512, [ 64, 'HMAC-SHA512' ],
182 };
183
184 if (!exists $key_len->{$this->{_auth_protocol}}) {
185@@ -1783,8 +1826,12 @@ sub _password_localize
186
187 my $digests =
188 {
189- AUTH_PROTOCOL_HMACMD5, 'Digest::MD5',
190- AUTH_PROTOCOL_HMACSHA, 'Digest::SHA',
191+ AUTH_PROTOCOL_HMACMD5, ['Digest::MD5', ],
192+ AUTH_PROTOCOL_HMACSHA, ['Digest::SHA', 1],
193+ AUTH_PROTOCOL_HMACSHA224, ['Digest::SHA', 224],
194+ AUTH_PROTOCOL_HMACSHA256, ['Digest::SHA', 256],
195+ AUTH_PROTOCOL_HMACSHA384, ['Digest::SHA', 384],
196+ AUTH_PROTOCOL_HMACSHA512, ['Digest::SHA', 512],
197 };
198
199 if (!exists $digests->{$this->{_auth_protocol}}) {
200@@ -1793,7 +1840,12 @@ sub _password_localize
201 );
202 }
203
204- my $digest = $digests->{$this->{_auth_protocol}}->new;
205+ my $digest;
206+ if (!defined($digests->{$this->{_auth_protocol}}[1])) {
207+ $digest = $digests->{$this->{_auth_protocol}}[0]->new;
208+ } else {
209+ $digest = $digests->{$this->{_auth_protocol}}[0]->new($digests->{$this->{_auth_protocol}}[1]);
210+ }
211
212 # Create the initial digest using the password
213
214diff --git a/t/usm-sha224-aes.t b/t/usm-sha224-aes.t
215new file mode 100644
216index 0000000..e7d118e
217--- /dev/null
218+++ b/t/usm-sha224-aes.t
219@@ -0,0 +1,169 @@
220+# -*- mode: perl -*-
221+# ============================================================================
222+
223+# Test of the SNMPv3 User-based Security Model.
224+
225+# Copyright (c) 2001-2009 David M. Town <dtown@cpan.org>.
226+# Copyright (c) 2024 Michal Josef Špaček <skim@cpan.org>.
227+# All rights reserved.
228+
229+# This program is free software; you may redistribute it and/or modify it
230+# under the same terms as the Perl 5 programming language system itself.
231+
232+# ============================================================================
233+
234+use strict;
235+use Test;
236+
237+BEGIN
238+{
239+ $| = 1;
240+ $^W = 1;
241+ plan tests => 7
242+}
243+
244+use Net::SNMP::Message qw(SEQUENCE OCTET_STRING FALSE);
245+
246+#
247+# Load the Net::SNMP::Security::USM module
248+#
249+
250+eval 'use Net::SNMP::Security::USM; use Crypt::Rijndael;';
251+
252+my $skip = ($@ =~ /locate (:?\S+\.pm)/) ? $@ : FALSE;
253+
254+#
255+# 1. Create the Net::SNMP::Security::USM object
256+#
257+
258+my ($u, $e);
259+
260+eval
261+{
262+ ($u, $e) = Net::SNMP::Security::USM->new(
263+ -username => 'dtown',
264+ -authpassword => 'maplesyrup',
265+ -authprotocol => 'sha224',
266+ -privpassword => 'maplesyrup',
267+ -privprotocol => 'aes',
268+ );
269+
270+ # "Perform" discovery...
271+ $u->_engine_id_discovery(pack 'x11H2', '02');
272+
273+ # ...and synchronization
274+ $u->_synchronize(10, time);
275+};
276+
277+skip(
278+ $skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Security::USM object'
279+);
280+
281+#
282+# 2. Check the localized authKey
283+#
284+
285+eval
286+{
287+ $e = unpack 'H*', $u->auth_key();
288+};
289+
290+skip(
291+ $skip,
292+ ($@ || $e),
293+ '0bd8827c6e29f8065e08e09237f177e410f69b90e1782be682075674',
294+ 'Invalid authKey calculated'
295+);
296+
297+#
298+# 3. Check the localized privKey
299+#
300+
301+eval
302+{
303+ $e = unpack 'H*', $u->priv_key();
304+};
305+
306+skip(
307+ $skip,
308+ ($@ || $e),
309+ '0bd8827c6e29f8065e08e09237f177e4',
310+ 'Invalid privKey calculated'
311+);
312+
313+#
314+# 4. Create and initialize a Message
315+#
316+
317+my $m;
318+
319+eval
320+{
321+ ($m, $e) = Net::SNMP::Message->new();
322+ $m->prepare(SEQUENCE, pack('H*', 'deadbeef') x 8);
323+ $e = $m->error();
324+};
325+
326+skip($skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Message object');
327+
328+#
329+# 5. Calculate the HMAC
330+#
331+
332+my $h;
333+
334+eval
335+{
336+ $h = unpack 'H*', $u->_auth_hmac($m);
337+};
338+
339+skip($skip, $@, q{}, 'Calculate the HMAC failed');
340+
341+#
342+# 6. Encrypt/descrypt the Message
343+#
344+
345+my $henc;
346+
347+eval
348+{
349+ my $engine_boots = 0;
350+ my $engine_time = 1710186219;
351+ my $salt;
352+ my $len = $m->length();
353+ my $buff = $m->clear();
354+ $u->{_engine_boots} = $engine_boots;
355+ $u->{_engine_time} = $engine_time;
356+ my $encrypted = $u->_encrypt_data($m, $salt, $buff);
357+ $henc = unpack 'H*', $encrypted;
358+ $m->append($encrypted);
359+ substr $salt, 0, 0, pack 'NN', $engine_boots, $engine_time;
360+ $u->_decrypt_data($m, $salt, $m->process(OCTET_STRING));
361+ $e = $u->error();
362+ # Remove padding if necessary
363+ if ($len -= $m->length()) {
364+ substr ${$m->reference()}, $len, -$len, q{};
365+ }
366+};
367+
368+skip(
369+ $skip,
370+ ($@ || $e || $henc),
371+ '042228c5467793ab001f4be546de4b990f90998b09d43f2baaffb52a5d36457cef3b5ab7',
372+ 'Privacy failed',
373+);
374+
375+#
376+# 7. Check the HMAC
377+#
378+
379+my $h2;
380+
381+eval
382+{
383+ $h2 = unpack 'H*', $u->_auth_hmac($m);
384+};
385+
386+skip($skip, ($@ || $h2), $h, 'Authentication failed');
387+
388+# ============================================================================
389diff --git a/t/usm-sha256-aes.t b/t/usm-sha256-aes.t
390new file mode 100644
391index 0000000..4521187
392--- /dev/null
393+++ b/t/usm-sha256-aes.t
394@@ -0,0 +1,169 @@
395+# -*- mode: perl -*-
396+# ============================================================================
397+
398+# Test of the SNMPv3 User-based Security Model.
399+
400+# Copyright (c) 2001-2009 David M. Town <dtown@cpan.org>.
401+# Copyright (c) 2024 Michal Josef Špaček <skim@cpan.org>.
402+# All rights reserved.
403+
404+# This program is free software; you may redistribute it and/or modify it
405+# under the same terms as the Perl 5 programming language system itself.
406+
407+# ============================================================================
408+
409+use strict;
410+use Test;
411+
412+BEGIN
413+{
414+ $| = 1;
415+ $^W = 1;
416+ plan tests => 7
417+}
418+
419+use Net::SNMP::Message qw(SEQUENCE OCTET_STRING FALSE);
420+
421+#
422+# Load the Net::SNMP::Security::USM module
423+#
424+
425+eval 'use Net::SNMP::Security::USM; use Crypt::Rijndael;';
426+
427+my $skip = ($@ =~ /locate (:?\S+\.pm)/) ? $@ : FALSE;
428+
429+#
430+# 1. Create the Net::SNMP::Security::USM object
431+#
432+
433+my ($u, $e);
434+
435+eval
436+{
437+ ($u, $e) = Net::SNMP::Security::USM->new(
438+ -username => 'dtown',
439+ -authpassword => 'maplesyrup',
440+ -authprotocol => 'sha256',
441+ -privpassword => 'maplesyrup',
442+ -privprotocol => 'aes',
443+ );
444+
445+ # "Perform" discovery...
446+ $u->_engine_id_discovery(pack 'x11H2', '02');
447+
448+ # ...and synchronization
449+ $u->_synchronize(10, time);
450+};
451+
452+skip(
453+ $skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Security::USM object'
454+);
455+
456+#
457+# 2. Check the localized authKey
458+#
459+
460+eval
461+{
462+ $e = unpack 'H*', $u->auth_key();
463+};
464+
465+skip(
466+ $skip,
467+ ($@ || $e),
468+ '8982e0e549e866db361a6b625d84cccc11162d453ee8ce3a6445c2d6776f0f8b',
469+ 'Invalid authKey calculated'
470+);
471+
472+#
473+# 3. Check the localized privKey
474+#
475+
476+eval
477+{
478+ $e = unpack 'H*', $u->priv_key();
479+};
480+
481+skip(
482+ $skip,
483+ ($@ || $e),
484+ '8982e0e549e866db361a6b625d84cccc',
485+ 'Invalid privKey calculated'
486+);
487+
488+#
489+# 4. Create and initialize a Message
490+#
491+
492+my $m;
493+
494+eval
495+{
496+ ($m, $e) = Net::SNMP::Message->new();
497+ $m->prepare(SEQUENCE, pack('H*', 'deadbeef') x 8);
498+ $e = $m->error();
499+};
500+
501+skip($skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Message object');
502+
503+#
504+# 5. Calculate the HMAC
505+#
506+
507+my $h;
508+
509+eval
510+{
511+ $h = unpack 'H*', $u->_auth_hmac($m);
512+};
513+
514+skip($skip, $@, q{}, 'Calculate the HMAC failed');
515+
516+#
517+# 6. Encrypt/descrypt the Message
518+#
519+
520+my $henc;
521+
522+eval
523+{
524+ my $engine_boots = 0;
525+ my $engine_time = 1710186219;
526+ my $salt;
527+ my $len = $m->length();
528+ my $buff = $m->clear();
529+ $u->{_engine_boots} = $engine_boots;
530+ $u->{_engine_time} = $engine_time;
531+ my $encrypted = $u->_encrypt_data($m, $salt, $buff);
532+ $henc = unpack 'H*', $encrypted;
533+ $m->append($encrypted);
534+ substr $salt, 0, 0, pack 'NN', $engine_boots, $engine_time;
535+ $u->_decrypt_data($m, $salt, $m->process(OCTET_STRING));
536+ $e = $u->error();
537+ # Remove padding if necessary
538+ if ($len -= $m->length()) {
539+ substr ${$m->reference()}, $len, -$len, q{};
540+ }
541+};
542+
543+skip(
544+ $skip,
545+ ($@ || $e || $henc),
546+ '0422863d06faf464369a298b66062e54374ba3b4a0f862162d25ba72130038f8befc9e21',
547+ 'Privacy failed',
548+);
549+
550+#
551+# 7. Check the HMAC
552+#
553+
554+my $h2;
555+
556+eval
557+{
558+ $h2 = unpack 'H*', $u->_auth_hmac($m);
559+};
560+
561+skip($skip, ($@ || $h2), $h, 'Authentication failed');
562+
563+# ============================================================================
564diff --git a/t/usm-sha384-aes.t b/t/usm-sha384-aes.t
565new file mode 100644
566index 0000000..e88ca4a
567--- /dev/null
568+++ b/t/usm-sha384-aes.t
569@@ -0,0 +1,169 @@
570+# -*- mode: perl -*-
571+# ============================================================================
572+
573+# Test of the SNMPv3 User-based Security Model.
574+
575+# Copyright (c) 2001-2009 David M. Town <dtown@cpan.org>.
576+# Copyright (c) 2024 Michal Josef Špaček <skim@cpan.org>.
577+# All rights reserved.
578+
579+# This program is free software; you may redistribute it and/or modify it
580+# under the same terms as the Perl 5 programming language system itself.
581+
582+# ============================================================================
583+
584+use strict;
585+use Test;
586+
587+BEGIN
588+{
589+ $| = 1;
590+ $^W = 1;
591+ plan tests => 7
592+}
593+
594+use Net::SNMP::Message qw(SEQUENCE OCTET_STRING FALSE);
595+
596+#
597+# Load the Net::SNMP::Security::USM module
598+#
599+
600+eval 'use Net::SNMP::Security::USM; use Crypt::Rijndael;';
601+
602+my $skip = ($@ =~ /locate (:?\S+\.pm)/) ? $@ : FALSE;
603+
604+#
605+# 1. Create the Net::SNMP::Security::USM object
606+#
607+
608+my ($u, $e);
609+
610+eval
611+{
612+ ($u, $e) = Net::SNMP::Security::USM->new(
613+ -username => 'dtown',
614+ -authpassword => 'maplesyrup',
615+ -authprotocol => 'sha384',
616+ -privpassword => 'maplesyrup',
617+ -privprotocol => 'aes',
618+ );
619+
620+ # "Perform" discovery...
621+ $u->_engine_id_discovery(pack 'x11H2', '02');
622+
623+ # ...and synchronization
624+ $u->_synchronize(10, time);
625+};
626+
627+skip(
628+ $skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Security::USM object'
629+);
630+
631+#
632+# 2. Check the localized authKey
633+#
634+
635+eval
636+{
637+ $e = unpack 'H*', $u->auth_key();
638+};
639+
640+skip(
641+ $skip,
642+ ($@ || $e),
643+ '3b298f16164a11184279d5432bf169e2d2a48307de02b3d3f7e2b4f36eb6f0455a53689a3937eea07319a633d2ccba78',
644+ 'Invalid authKey calculated'
645+);
646+
647+#
648+# 3. Check the localized privKey
649+#
650+
651+eval
652+{
653+ $e = unpack 'H*', $u->priv_key();
654+};
655+
656+skip(
657+ $skip,
658+ ($@ || $e),
659+ '3b298f16164a11184279d5432bf169e2',
660+ 'Invalid privKey calculated'
661+);
662+
663+#
664+# 4. Create and initialize a Message
665+#
666+
667+my $m;
668+
669+eval
670+{
671+ ($m, $e) = Net::SNMP::Message->new();
672+ $m->prepare(SEQUENCE, pack('H*', 'deadbeef') x 8);
673+ $e = $m->error();
674+};
675+
676+skip($skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Message object');
677+
678+#
679+# 5. Calculate the HMAC
680+#
681+
682+my $h;
683+
684+eval
685+{
686+ $h = unpack 'H*', $u->_auth_hmac($m);
687+};
688+
689+skip($skip, $@, q{}, 'Calculate the HMAC failed');
690+
691+#
692+# 6. Encrypt/descrypt the Message
693+#
694+
695+my $henc;
696+
697+eval
698+{
699+ my $engine_boots = 0;
700+ my $engine_time = 1710186219;
701+ my $salt;
702+ my $len = $m->length();
703+ my $buff = $m->clear();
704+ $u->{_engine_boots} = $engine_boots;
705+ $u->{_engine_time} = $engine_time;
706+ my $encrypted = $u->_encrypt_data($m, $salt, $buff);
707+ $henc = unpack 'H*', $encrypted;
708+ $m->append($encrypted);
709+ substr $salt, 0, 0, pack 'NN', $engine_boots, $engine_time;
710+ $u->_decrypt_data($m, $salt, $m->process(OCTET_STRING));
711+ $e = $u->error();
712+ # Remove padding if necessary
713+ if ($len -= $m->length()) {
714+ substr ${$m->reference()}, $len, -$len, q{};
715+ }
716+};
717+
718+skip(
719+ $skip,
720+ ($@ || $e || $henc),
721+ '0422850967bbff81be49aefadf9b6ee3eedb9093609fcfc813a21bdf09b469965923bfc0',
722+ 'Privacy failed',
723+);
724+
725+#
726+# 7. Check the HMAC
727+#
728+
729+my $h2;
730+
731+eval
732+{
733+ $h2 = unpack 'H*', $u->_auth_hmac($m);
734+};
735+
736+skip($skip, ($@ || $h2), $h, 'Authentication failed');
737+
738+# ============================================================================
739diff --git a/t/usm-sha512-aes.t b/t/usm-sha512-aes.t
740new file mode 100644
741index 0000000..c5eadf3
742--- /dev/null
743+++ b/t/usm-sha512-aes.t
744@@ -0,0 +1,169 @@
745+# -*- mode: perl -*-
746+# ============================================================================
747+
748+# Test of the SNMPv3 User-based Security Model.
749+
750+# Copyright (c) 2001-2009 David M. Town <dtown@cpan.org>.
751+# Copyright (c) 2024 Michal Josef Špaček <skim@cpan.org>.
752+# All rights reserved.
753+
754+# This program is free software; you may redistribute it and/or modify it
755+# under the same terms as the Perl 5 programming language system itself.
756+
757+# ============================================================================
758+
759+use strict;
760+use Test;
761+
762+BEGIN
763+{
764+ $| = 1;
765+ $^W = 1;
766+ plan tests => 7
767+}
768+
769+use Net::SNMP::Message qw(SEQUENCE OCTET_STRING FALSE);
770+
771+#
772+# Load the Net::SNMP::Security::USM module
773+#
774+
775+eval 'use Net::SNMP::Security::USM; use Crypt::Rijndael;';
776+
777+my $skip = ($@ =~ /locate (:?\S+\.pm)/) ? $@ : FALSE;
778+
779+#
780+# 1. Create the Net::SNMP::Security::USM object
781+#
782+
783+my ($u, $e);
784+
785+eval
786+{
787+ ($u, $e) = Net::SNMP::Security::USM->new(
788+ -username => 'dtown',
789+ -authpassword => 'maplesyrup',
790+ -authprotocol => 'sha512',
791+ -privpassword => 'maplesyrup',
792+ -privprotocol => 'aes',
793+ );
794+
795+ # "Perform" discovery...
796+ $u->_engine_id_discovery(pack 'x11H2', '02');
797+
798+ # ...and synchronization
799+ $u->_synchronize(10, time);
800+};
801+
802+skip(
803+ $skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Security::USM object'
804+);
805+
806+#
807+# 2. Check the localized authKey
808+#
809+
810+eval
811+{
812+ $e = unpack 'H*', $u->auth_key();
813+};
814+
815+skip(
816+ $skip,
817+ ($@ || $e),
818+ '22a5a36cedfcc085807a128d7bc6c2382167ad6c0dbc5fdff856740f3d84c099ad1ea87a8db096714d9788bd544047c9021e4229ce27e4c0a69250adfcffbb0b',
819+ 'Invalid authKey calculated'
820+);
821+
822+#
823+# 3. Check the localized privKey
824+#
825+
826+eval
827+{
828+ $e = unpack 'H*', $u->priv_key();
829+};
830+
831+skip(
832+ $skip,
833+ ($@ || $e),
834+ '22a5a36cedfcc085807a128d7bc6c238',
835+ 'Invalid privKey calculated'
836+);
837+
838+#
839+# 4. Create and initialize a Message
840+#
841+
842+my $m;
843+
844+eval
845+{
846+ ($m, $e) = Net::SNMP::Message->new();
847+ $m->prepare(SEQUENCE, pack('H*', 'deadbeef') x 8);
848+ $e = $m->error();
849+};
850+
851+skip($skip, ($@ || $e), q{}, 'Failed to create Net::SNMP::Message object');
852+
853+#
854+# 5. Calculate the HMAC
855+#
856+
857+my $h;
858+
859+eval
860+{
861+ $h = unpack 'H*', $u->_auth_hmac($m);
862+};
863+
864+skip($skip, $@, q{}, 'Calculate the HMAC failed');
865+
866+#
867+# 6. Encrypt/descrypt the Message
868+#
869+
870+my $henc;
871+
872+eval
873+{
874+ my $engine_boots = 0;
875+ my $engine_time = 1710186219;
876+ my $salt;
877+ my $len = $m->length();
878+ my $buff = $m->clear();
879+ $u->{_engine_boots} = $engine_boots;
880+ $u->{_engine_time} = $engine_time;
881+ my $encrypted = $u->_encrypt_data($m, $salt, $buff);
882+ $henc = unpack 'H*', $encrypted;
883+ $m->append($encrypted);
884+ substr $salt, 0, 0, pack 'NN', $engine_boots, $engine_time;
885+ $u->_decrypt_data($m, $salt, $m->process(OCTET_STRING));
886+ $e = $u->error();
887+ # Remove padding if necessary
888+ if ($len -= $m->length()) {
889+ substr ${$m->reference()}, $len, -$len, q{};
890+ }
891+};
892+
893+skip(
894+ $skip,
895+ ($@ || $e || $henc),
896+ '0422bae4cdb263a0936b189051e9b1759183b16d07c9a7d8bf633d6dd2e817a2ac2fe742',
897+ 'Privacy failed',
898+);
899+
900+#
901+# 7. Check the HMAC
902+#
903+
904+my $h2;
905+
906+eval
907+{
908+ $h2 = unpack 'H*', $u->_auth_hmac($m);
909+};
910+
911+skip($skip, ($@ || $h2), $h, 'Authentication failed');
912+
913+# ============================================================================