public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Paul Wouters <paul.wouters@aiven.io>
To: git-commits@fedoraproject.org
Subject: [rpms/strongswan] rawhide: - Add upstream patch for MLDSA to facilitate interop testing
Date: Mon, 10 Aug 2026 14:02:46 GMT	[thread overview]
Message-ID: <178637056664.1.3369289560107150870.rpms-strongswan-f7dc5ca86a43@fedoraproject.org> (raw)

A new commit has been pushed.

Repo   : rpms/strongswan
Branch : rawhide
Commit : f7dc5ca86a43426bb928b5db2bc729697a6b4513
Author : Paul Wouters <paul.wouters@aiven.io>
Date   : 2026-08-10T10:02:21-04:00
Stats  : +12379/-0 in 2 file(s)
URL    : https://src.fedoraproject.org/rpms/strongswan/c/f7dc5ca86a43426bb928b5db2bc729697a6b4513?branch=rawhide

Log:
- Add upstream patch for MLDSA to facilitate interop testing

---
diff --git a/strongswan-mldsa.patch b/strongswan-mldsa.patch
new file mode 100644
index 0000000..d5bbd59
--- /dev/null
+++ b/strongswan-mldsa.patch
@@ -0,0 +1,12378 @@
+diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c
+index 866b3c85758..d7216f50e09 100644
+--- a/src/libstrongswan/asn1/asn1.c
++++ b/src/libstrongswan/asn1/asn1.c
+@@ -58,6 +58,9 @@ chunk_t asn1_algorithmIdentifier(int oid)
+ 		case OID_ECDSA_WITH_SHA512:
+ 		case OID_ED25519:
+ 		case OID_ED448:
++		case OID_ML_DSA_44:
++		case OID_ML_DSA_65:
++		case OID_ML_DSA_87:
+ 			parameters = chunk_empty;
+ 			break;
+ 		default:
+diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt
+index 8fcfe535247..fc29029e5c3 100644
+--- a/src/libstrongswan/asn1/oid.txt
++++ b/src/libstrongswan/asn1/oid.txt
+@@ -451,6 +451,12 @@
+                 0x0E         "id-rsassa-pkcs1v15-with-sha3-256"	OID_RSASSA_PKCS1V15_WITH_SHA3_256
+                 0x0F         "id-rsassa-pkcs1v15-with-sha3-384"	OID_RSASSA_PKCS1V15_WITH_SHA3_384
+                 0x10         "id-rsassa-pkcs1v15-with-sha3-512"	OID_RSASSA_PKCS1V15_WITH_SHA3_512
++                0x11         "id-ml-dsa-44" OID_ML_DSA_44
++                0x12         "id-ml-dsa-65" OID_ML_DSA_65
++                0x13         "id-ml-dsa-87" OID_ML_DSA_87
++                0x20         "id-hash-ml-dsa-44-with-sha512"
++                0x21         "id-hash-ml-dsa-65-with-sha512"
++                0x22         "id-hash-ml-dsa-87-with-sha512"
+         0x86                 ""
+           0xf8               ""
+             0x42             "netscape"
+diff --git a/src/libstrongswan/credentials/auth_cfg.c b/src/libstrongswan/credentials/auth_cfg.c
+index fb052ca90f8..184d4b835ae 100644
+--- a/src/libstrongswan/credentials/auth_cfg.c
++++ b/src/libstrongswan/credentials/auth_cfg.c
+@@ -1,7 +1,7 @@
+ /*
+  * Copyright (C) 2008-2017 Tobias Brunner
+  * Copyright (C) 2007-2009 Martin Willi
+- * Copyright (C) 2016 Andreas Steffen
++ * Copyright (C) 2016-2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -615,21 +615,24 @@ METHOD(auth_cfg_t, add_pubkey_constraints, void,
+ 			signature_scheme_t scheme;
+ 			key_type_t key;
+ 		} schemes[] = {
+-			{ "md5",		SIGN_RSA_EMSA_PKCS1_MD5,		KEY_RSA,	 },
+-			{ "sha1",		SIGN_RSA_EMSA_PKCS1_SHA1,		KEY_RSA,	 },
+-			{ "sha224",		SIGN_RSA_EMSA_PKCS1_SHA2_224,	KEY_RSA,	 },
+-			{ "sha256",		SIGN_RSA_EMSA_PKCS1_SHA2_256,	KEY_RSA,	 },
+-			{ "sha384",		SIGN_RSA_EMSA_PKCS1_SHA2_384,	KEY_RSA,	 },
+-			{ "sha512",		SIGN_RSA_EMSA_PKCS1_SHA2_512,	KEY_RSA,	 },
+-			{ "sha1",		SIGN_ECDSA_WITH_SHA1_DER,		KEY_ECDSA,	 },
+-			{ "sha256",		SIGN_ECDSA_WITH_SHA256_DER,		KEY_ECDSA,	 },
+-			{ "sha384",		SIGN_ECDSA_WITH_SHA384_DER,		KEY_ECDSA,	 },
+-			{ "sha512",		SIGN_ECDSA_WITH_SHA512_DER,		KEY_ECDSA,	 },
+-			{ "sha256",		SIGN_ECDSA_256,					KEY_ECDSA,	 },
+-			{ "sha384",		SIGN_ECDSA_384,					KEY_ECDSA,	 },
+-			{ "sha512",		SIGN_ECDSA_521,					KEY_ECDSA,	 },
+-			{ "identity",	SIGN_ED25519,					KEY_ED25519, },
+-			{ "identity",	SIGN_ED448,						KEY_ED448,	 },
++			{ "md5",		SIGN_RSA_EMSA_PKCS1_MD5,		KEY_RSA,	   },
++			{ "sha1",		SIGN_RSA_EMSA_PKCS1_SHA1,		KEY_RSA,	   },
++			{ "sha224",		SIGN_RSA_EMSA_PKCS1_SHA2_224,	KEY_RSA,	   },
++			{ "sha256",		SIGN_RSA_EMSA_PKCS1_SHA2_256,	KEY_RSA,	   },
++			{ "sha384",		SIGN_RSA_EMSA_PKCS1_SHA2_384,	KEY_RSA,	   },
++			{ "sha512",		SIGN_RSA_EMSA_PKCS1_SHA2_512,	KEY_RSA,	   },
++			{ "sha1",		SIGN_ECDSA_WITH_SHA1_DER,		KEY_ECDSA,	   },
++			{ "sha256",		SIGN_ECDSA_WITH_SHA256_DER,		KEY_ECDSA,	   },
++			{ "sha384",		SIGN_ECDSA_WITH_SHA384_DER,		KEY_ECDSA,	   },
++			{ "sha512",		SIGN_ECDSA_WITH_SHA512_DER,		KEY_ECDSA,	   },
++			{ "sha256",		SIGN_ECDSA_256,					KEY_ECDSA,	   },
++			{ "sha384",		SIGN_ECDSA_384,					KEY_ECDSA,	   },
++			{ "sha512",		SIGN_ECDSA_521,					KEY_ECDSA,	   },
++			{ "identity",	SIGN_ED25519,					KEY_ED25519,   },
++			{ "identity",	SIGN_ED448,						KEY_ED448,	   },
++			{ "identity",	SIGN_ML_DSA_44,					KEY_ML_DSA_44, },
++			{ "identity",	SIGN_ML_DSA_65,					KEY_ML_DSA_65, },
++			{ "identity",	SIGN_ML_DSA_87,					KEY_ML_DSA_87, },
+ 		};
+ 
+ 		if (expected_strength != AUTH_RULE_MAX)
+@@ -678,6 +681,24 @@ METHOD(auth_cfg_t, add_pubkey_constraints, void,
+ 			expected_type = KEY_ED448;
+ 			continue;
+ 		}
++		if (streq(token, "mldsa44") || streq(token, "ike:mldsa44"))
++		{
++			key_token = token;
++			expected_type = KEY_ML_DSA_44;
++			continue;
++		}
++		if (streq(token, "mldsa65") || streq(token, "ike:mldsa65"))
++		{
++			key_token = token;
++			expected_type = KEY_ML_DSA_65;
++			continue;
++		}
++		if (streq(token, "mldsa87") || streq(token, "ike:mldsa87"))
++		{
++			key_token = token;
++			expected_type = KEY_ML_DSA_87;
++			continue;
++		}
+ 		if (streq(token, "pubkey") || streq(token, "ike:pubkey"))
+ 		{
+ 			key_token = token;
+diff --git a/src/libstrongswan/credentials/builder.c b/src/libstrongswan/credentials/builder.c
+index 76bcd3917e7..9b28d7af1ee 100644
+--- a/src/libstrongswan/credentials/builder.c
++++ b/src/libstrongswan/credentials/builder.c
+@@ -1,6 +1,6 @@
+ /*
+  * Copyright (C) 2008 Martin Willi
+- * Copyright (C) 2016-2023 Andreas Steffen
++ * Copyright (C) 2016-2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+diff --git a/src/libstrongswan/credentials/builder.h b/src/libstrongswan/credentials/builder.h
+index 7e2a239a33f..41ed35e3b8b 100644
+--- a/src/libstrongswan/credentials/builder.h
++++ b/src/libstrongswan/credentials/builder.h
+@@ -1,6 +1,6 @@
+ /*
+  * Copyright (C) 2008 Martin Willi
+- * Copyright (C) 2016-2022 Andreas Steffen
++ * Copyright (C) 2016-2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+diff --git a/src/libstrongswan/credentials/cred_encoding.h b/src/libstrongswan/credentials/cred_encoding.h
+index 3f0b5f1b63a..41fadfcf1fa 100644
+--- a/src/libstrongswan/credentials/cred_encoding.h
++++ b/src/libstrongswan/credentials/cred_encoding.h
+@@ -145,6 +145,10 @@ enum cred_encoding_part_t {
+ 	CRED_PART_PKCS10_ASN1_DER,
+ 	/** a PGP encoded certificate */
+ 	CRED_PART_PGP_CERT,
++	/** a DER encoded public key */
++	CRED_PART_PUB_ASN1_DER,
++	/** a DER encoded private key */
++	CRED_PART_PRIV_ASN1_DER,
+ 	/** a DER encoded EdDSA public key */
+ 	CRED_PART_EDDSA_PUB_ASN1_DER,
+ 	/** a DER encoded EdDSA private key */
+diff --git a/src/libstrongswan/credentials/keys/public_key.c b/src/libstrongswan/credentials/keys/public_key.c
+index f83782d8204..c7afcc01d81 100644
+--- a/src/libstrongswan/credentials/keys/public_key.c
++++ b/src/libstrongswan/credentials/keys/public_key.c
+@@ -1,6 +1,6 @@
+ /*
+  * Copyright (C) 2015-2017 Tobias Brunner
+- * Copyright (C) 2014-2020 Andreas Steffen
++ * Copyright (C) 2014-2024 Andreas Steffen
+  * Copyright (C) 2007 Martin Willi
+  *
+  * Copyright (C) secunet Security Networks AG
+@@ -17,20 +17,25 @@
+  */
+ 
+ #include <asn1/oid.h>
++#include <asn1/asn1.h>
++#include <asn1/asn1_parser.h>
+ 
+ #include "public_key.h"
+ #include "signature_params.h"
+ 
+-ENUM(key_type_names, KEY_ANY, KEY_ED448,
++ENUM(key_type_names, KEY_ANY, KEY_ML_DSA_87,
+ 	"ANY",
+ 	"RSA",
+ 	"ECDSA",
+ 	"DSA",
+ 	"ED25519",
+ 	"ED448",
++	"ML_DSA_44",
++	"ML_DSA_65",
++	"ML_DSA_87",
+ );
+ 
+-ENUM(signature_scheme_names, SIGN_UNKNOWN, SIGN_ED448,
++ENUM(signature_scheme_names, SIGN_UNKNOWN, SIGN_ML_DSA_87,
+ 	"UNKNOWN",
+ 	"RSA_EMSA_PKCS1_NULL",
+ 	"RSA_EMSA_PKCS1_MD5",
+@@ -54,6 +59,9 @@ ENUM(signature_scheme_names, SIGN_UNKNOWN, SIGN_ED448,
+ 	"ECDSA-521",
+ 	"ED25519",
+ 	"ED448",
++	"ML_DSA_44",
++	"ML_DSA_65",
++	"ML_DSA_87",
+ );
+ 
+ ENUM(encryption_scheme_names, ENCRYPT_UNKNOWN, ENCRYPT_RSA_OAEP_SHA512,
+@@ -109,6 +117,120 @@ bool public_key_has_fingerprint(public_key_t *public, chunk_t fingerprint)
+ 	return FALSE;
+ }
+ 
++/**
++ * See header.
++ */
++chunk_t public_key_info_encode(chunk_t pubkey, int oid)
++{
++	return asn1_wrap(ASN1_SEQUENCE, "mm",
++						asn1_algorithmIdentifier(oid),
++						asn1_bitstring("c", pubkey)
++					 );
++}
++
++/**
++ * ASN.1 definition of a subjectPublicKeyInfo structure
++ */
++static const asn1Object_t pkinfoObjects[] = {
++	{ 0, "subjectPublicKeyInfo",ASN1_SEQUENCE,		ASN1_NONE	}, /* 0 */
++	{ 1,   "algorithm",			ASN1_EOC,			ASN1_RAW	}, /* 1 */
++	{ 1,   "subjectPublicKey",	ASN1_BIT_STRING,	ASN1_BODY	}, /* 2 */
++	{ 0, "exit",				ASN1_EOC,			ASN1_EXIT	}
++};
++#define PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM	1
++#define PKINFO_SUBJECT_PUBLIC_KEY			2
++
++/**
++ * See header.
++ */
++key_type_t public_key_info_decode(chunk_t pkcs1, chunk_t *pubkey)
++{
++	asn1_parser_t *parser;
++	int oid = OID_UNKNOWN;
++	chunk_t object;
++	int objectID;
++	bool success;
++
++	parser = asn1_parser_create(pkinfoObjects, pkcs1);
++	while (parser->iterate(parser, &objectID, &object))
++	{
++		switch (objectID)
++		{
++			case PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM:
++				oid = asn1_parse_algorithmIdentifier(object,
++							parser->get_level(parser)+1, NULL);
++				break;
++			case PKINFO_SUBJECT_PUBLIC_KEY:
++				if (pubkey)
++				{
++					if (object.len > 0 && *object.ptr == 0x00)
++					{
++						/* skip first bit string byte defining 0 unused bits */
++						object = chunk_skip(object, 1);
++					}
++					*pubkey = object;
++				}
++				break;
++		}
++	}
++	success = parser->success(parser);
++	parser->destroy(parser);
++
++	return success ? key_type_from_oid(oid) : KEY_ANY;
++}
++
++/**
++ * See header.
++ */
++int key_type_to_oid(key_type_t type)
++{
++	switch (type)
++	{
++		case KEY_ED25519:
++			return OID_ED25519;
++		case KEY_ED448:
++			return OID_ED448;
++		case KEY_ML_DSA_44:
++			return OID_ML_DSA_44;
++		case KEY_ML_DSA_65:
++			return OID_ML_DSA_65;
++		case KEY_ML_DSA_87:
++			return OID_ML_DSA_87;
++		case KEY_ECDSA:
++			return OID_EC_PUBLICKEY;
++		default:
++			return OID_UNKNOWN;
++	}
++}
++
++/**
++ * See header.
++ */
++key_type_t key_type_from_oid(int oid)
++{
++	switch (oid)
++	{
++		case OID_ED25519:
++			return KEY_ED25519;
++		case OID_ED448:
++			return KEY_ED448;
++		case OID_ML_DSA_44:
++			return KEY_ML_DSA_44;
++		case OID_ML_DSA_65:
++			return KEY_ML_DSA_65;
++		case OID_ML_DSA_87:
++			return KEY_ML_DSA_87;
++		case OID_EC_PUBLICKEY:
++			return KEY_ECDSA;
++		case OID_RSA_ENCRYPTION:
++		case OID_RSAES_OAEP:
++		case OID_RSASSA_PSS:
++			return KEY_RSA;
++		default:
++			return KEY_ANY;
++	}
++}
++
+ /*
+  * Defined in header.
+  */
+@@ -157,6 +279,12 @@ signature_scheme_t signature_scheme_from_oid(int oid)
+ 			return SIGN_ED25519;
+ 		case OID_ED448:
+ 			return SIGN_ED448;
++		case OID_ML_DSA_44:
++			return SIGN_ML_DSA_44;
++		case OID_ML_DSA_65:
++			return SIGN_ML_DSA_65;
++		case OID_ML_DSA_87:
++			return SIGN_ML_DSA_87;
+ 	}
+ 	return SIGN_UNKNOWN;
+ }
+@@ -209,6 +337,12 @@ int signature_scheme_to_oid(signature_scheme_t scheme)
+ 			return OID_ED25519;
+ 		case SIGN_ED448:
+ 			return OID_ED448;
++		case SIGN_ML_DSA_44:
++			return OID_ML_DSA_44;
++		case SIGN_ML_DSA_65:
++			return OID_ML_DSA_65;
++		case SIGN_ML_DSA_87:
++			return OID_ML_DSA_87;
+ 	}
+ 	return OID_UNKNOWN;
+ }
+@@ -236,17 +370,20 @@ static struct {
+ 	int max_keysize;
+ 	signature_params_t params;
+ } scheme_map[] = {
+-	{ KEY_RSA,  3072, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, }},
+-	{ KEY_RSA,  7680, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, }},
+-	{ KEY_RSA,     0, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha512, }},
+-	{ KEY_RSA,  3072, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 }},
+-	{ KEY_RSA,  7680, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_384 }},
+-	{ KEY_RSA,     0, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 }},
+-	{ KEY_ECDSA, 256, { .scheme = SIGN_ECDSA_WITH_SHA256_DER }},
+-	{ KEY_ECDSA, 384, { .scheme = SIGN_ECDSA_WITH_SHA384_DER }},
+-	{ KEY_ECDSA,   0, { .scheme = SIGN_ECDSA_WITH_SHA512_DER }},
+-	{ KEY_ED25519, 0, { .scheme = SIGN_ED25519 }},
+-	{ KEY_ED448,   0, { .scheme = SIGN_ED448 }},
++	{ KEY_RSA,    3072, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha256, }},
++	{ KEY_RSA,    7680, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha384, }},
++	{ KEY_RSA,       0, { .scheme = SIGN_RSA_EMSA_PSS, .params = &pss_params_sha512, }},
++	{ KEY_RSA,    3072, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_256 }},
++	{ KEY_RSA,    7680, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_384 }},
++	{ KEY_RSA,       0, { .scheme = SIGN_RSA_EMSA_PKCS1_SHA2_512 }},
++	{ KEY_ECDSA,   256, { .scheme = SIGN_ECDSA_WITH_SHA256_DER }},
++	{ KEY_ECDSA,   384, { .scheme = SIGN_ECDSA_WITH_SHA384_DER }},
++	{ KEY_ECDSA,     0, { .scheme = SIGN_ECDSA_WITH_SHA512_DER }},
++	{ KEY_ED25519,   0, { .scheme = SIGN_ED25519 }},
++	{ KEY_ED448,     0, { .scheme = SIGN_ED448 }},
++	{ KEY_ML_DSA_44, 0, { .scheme = SIGN_ML_DSA_44 }},
++	{ KEY_ML_DSA_65, 0, { .scheme = SIGN_ML_DSA_65 }},
++	{ KEY_ML_DSA_87, 0, { .scheme = SIGN_ML_DSA_87 }},
+ };
+ 
+ /**
+@@ -335,6 +472,34 @@ key_type_t key_type_from_signature_scheme(signature_scheme_t scheme)
+ 			return KEY_ED25519;
+ 		case SIGN_ED448:
+ 			return KEY_ED448;
++		case SIGN_ML_DSA_44:
++			return KEY_ML_DSA_44;
++		case SIGN_ML_DSA_65:
++			return KEY_ML_DSA_65;
++		case SIGN_ML_DSA_87:
++			return KEY_ML_DSA_87;
+ 	}
+ 	return KEY_ANY;
+ }
++
++/*
++ * Defined in header.
++ */
++int get_public_key_size(key_type_t type)
++{
++	switch (type)
++	{
++		case KEY_ED25519:
++			return   32;   /* bytes */
++		case KEY_ED448:
++			return   57;   /* bytes */
++		case KEY_ML_DSA_44:
++			return 1312;   /* bytes */
++		case KEY_ML_DSA_65:
++			return 1952;   /* bytes */
++		case KEY_ML_DSA_87:
++			return 2592;   /* bytes */
++		default:
++			return 0;
++	}
++}
+diff --git a/src/libstrongswan/credentials/keys/public_key.h b/src/libstrongswan/credentials/keys/public_key.h
+index 32a41cf9cba..283d880d40e 100644
+--- a/src/libstrongswan/credentials/keys/public_key.h
++++ b/src/libstrongswan/credentials/keys/public_key.h
+@@ -1,6 +1,6 @@
+ /*
+  * Copyright (C) 2015-2017 Tobias Brunner
+- * Copyright (C) 2014-2020 Andreas Steffen
++ * Copyright (C) 2014-2024 Andreas Steffen
+  * Copyright (C) 2007 Martin Willi
+  *
+  * Copyright (C) secunet Security Networks AG
+@@ -37,17 +37,23 @@ typedef enum encryption_scheme_t encryption_scheme_t;
+  */
+ enum key_type_t {
+ 	/** key type wildcard */
+-	KEY_ANY     = 0,
++	KEY_ANY       = 0,
+ 	/** RSA crypto system as in PKCS#1 */
+-	KEY_RSA     = 1,
++	KEY_RSA       = 1,
+ 	/** ECDSA as in ANSI X9.62 */
+-	KEY_ECDSA   = 2,
++	KEY_ECDSA     = 2,
+ 	/** DSA */
+-	KEY_DSA     = 3,
++	KEY_DSA       = 3,
+ 	/** Ed25519 PureEdDSA instance as in RFC 8032 */
+-	KEY_ED25519 = 4,
++	KEY_ED25519   = 4,
+ 	/** Ed448   PureEdDSA instance as in RFC 8032 */
+-	KEY_ED448   = 5,
++	KEY_ED448     = 5,
++	/** ML-DSA-44 as in FIPS 204 */
++	KEY_ML_DSA_44 = 6,
++	/** ML-DSA-65 as in FIPS 204 */
++	KEY_ML_DSA_65 = 7,
++	/** ML-DSA-87 as in FIPS 204 */
++	KEY_ML_DSA_87 = 8,
+ };
+ 
+ /**
+@@ -109,6 +115,12 @@ enum signature_scheme_t {
+ 	SIGN_ED25519,
+ 	/** PureEdDSA on Curve448 as in RFC 8410                           */
+ 	SIGN_ED448,
++	/** Pure ML-DSA-44 as in FIPS 204                                   */
++	SIGN_ML_DSA_44,
++	/** Pure ML-DSA-65 as in FIPS 204                                   */
++	SIGN_ML_DSA_65,
++	/** Pure ML-DSA-87 as in FIPS 204                                   */
++	SIGN_ML_DSA_87,
+ };
+ 
+ /**
+@@ -251,6 +263,40 @@ bool public_key_equals(public_key_t *public, public_key_t *other);
+  */
+ bool public_key_has_fingerprint(public_key_t *public, chunk_t fingerprint);
+ 
++/**
++ * ASN.1 encoding of public key info
++ *
++ * @param pubkey		public key blob
++ * @param oid			OID of the public key type
++ * @return				ASN.1 encoded public key info blob
++ */
++chunk_t public_key_info_encode(chunk_t pubkey, int oid);
++
++/**
++ * ASN.1 decoding of public key info
++ *
++ * @param pkcs1			ASN.1 encoded public key in PCKS#1 format
++ * @param pubkey		unwrapped public key blob
++ * @return				type of the key (KEY_ANY if failure)
++ */
++key_type_t public_key_info_decode(chunk_t pkcs1, chunk_t *pubkey);
++
++/**
++ * Return OID for a given key type
++ *
++ * @param type		type of the key
++ * @return			OID
++ */
++int key_type_to_oid(key_type_t type);
++
++/**
++ * Return key type for a given OID
++ *
++ * @param oid		OID
++ * @return			type of the key
++ */
++key_type_t key_type_from_oid(int oid);
++
+ /**
+  * Conversion of ASN.1 signature or hash OID to signature scheme.
+  *
+@@ -285,5 +331,12 @@ enumerator_t *signature_schemes_for_key(key_type_t type, int size);
+  */
+ key_type_t key_type_from_signature_scheme(signature_scheme_t scheme);
+ 
++/**
++ * Return the public key size in octets for key types with fixed sizes.
++ *
++ * @param type			type of the key
++ * @return				public key size in octets
++ */
++int get_public_key_size(key_type_t type);
+ 
+ #endif /** PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/credentials/keys/signature_params.c b/src/libstrongswan/credentials/keys/signature_params.c
+index 5a391df8930..44b6daacc9f 100644
+--- a/src/libstrongswan/credentials/keys/signature_params.c
++++ b/src/libstrongswan/credentials/keys/signature_params.c
+@@ -407,3 +407,50 @@ bool rsa_pss_params_build(rsa_pss_params_t *params, chunk_t *asn1)
+ 				trfd.len ? asn1_wrap(ASN1_CONTEXT_C_3, "m", trfd) : chunk_empty);
+ 	return TRUE;
+ }
++
++/**
++ * Maximum length of the context string.
++ */
++#define PQC_MAX_CTX_LEN  255
++
++/*
++ * Described in header
++ */
++bool pqc_params_create(pqc_params_t *p_in, pqc_params_t *p_out)
++{
++	if (p_in == NULL)
++	{
++		p_out->deterministic = FALSE;
++
++		p_out->pre_ctx = chunk_alloc(2);
++		p_out->pre_ctx.ptr[0] = 0x00;
++		p_out->pre_ctx.ptr[1] = 0x00;
++	}
++	else
++	{
++		p_out->deterministic = p_in->deterministic;
++
++		if (p_in->ctx.len > PQC_MAX_CTX_LEN)
++		{
++			DBG1(DBG_LIB, "error: context length of PQC signature is %u bytes, "
++						  "larger than the maximum of %u bytes",
++						  p_in->ctx.len, PQC_MAX_CTX_LEN);
++			return FALSE;
++		}
++		p_out->pre_ctx = chunk_alloc(2 + p_in->ctx.len);
++		p_out->pre_ctx.ptr[0] = 0x00;
++		p_out->pre_ctx.ptr[1] = (uint8_t)p_in->ctx.len;
++		memcpy(p_out->pre_ctx.ptr + 2, p_in->ctx.ptr, p_in->ctx.len);
++	}
++	p_out->ctx = chunk_skip(p_out->pre_ctx, 2);
++
++	return TRUE;
++}
++
++/*
++ * Described in header
++ */
++void pqc_params_free(pqc_params_t *params)
++{
++	chunk_free(&params->pre_ctx);
++}
+\ No newline at end of file
+diff --git a/src/libstrongswan/credentials/keys/signature_params.h b/src/libstrongswan/credentials/keys/signature_params.h
+index 19ce82c30d0..d93b8310432 100644
+--- a/src/libstrongswan/credentials/keys/signature_params.h
++++ b/src/libstrongswan/credentials/keys/signature_params.h
+@@ -24,6 +24,7 @@
+ 
+ typedef struct signature_params_t signature_params_t;
+ typedef struct rsa_pss_params_t rsa_pss_params_t;
++typedef struct pqc_params_t pqc_params_t;
+ 
+ #include <crypto/hashers/hasher.h>
+ 
+@@ -112,6 +113,18 @@ struct rsa_pss_params_t {
+ #define RSA_PSS_SALT_LEN_MAX -2
+ };
+ 
++/**
++ * Parameters for Post Quantum Cryptography (PQC) signature schemes
++ */
++struct pqc_params_t {
++	/** Use deterministic signature */
++	bool deterministic;
++	/** Context string */
++	chunk_t ctx;
++	/** Context string with length prefix */
++	chunk_t pre_ctx;
++};
++
+ /**
+  * Parse the given ASN.1 algorithm identifier params
+  *
+@@ -142,4 +155,20 @@ bool rsa_pss_params_build(rsa_pss_params_t *params, chunk_t *asn1);
+  */
+ bool rsa_pss_params_set_salt_len(rsa_pss_params_t *params, size_t modbits);
+ 
++/**
++ * Set PQC signature params, allocating context string with length prefix
++ *
++ * @param p_in		input PQC signature params, use defaults if NULL
++ * @param p_out		output PQC signature params
++ * @return			TRUE if successfully created
++ */
++bool pqc_params_create(pqc_params_t *p_in, pqc_params_t *p_out);
++
++/**
++ * Free allocated context string with length prefix in PQC signature params
++ *
++ * @param params	PQC signature params to be freed
++ */
++void pqc_params_free(pqc_params_t *params);
++
+ #endif /** SIGNATURE_PARAMS_H_ @}*/
+diff --git a/src/libstrongswan/crypto/hashers/hasher.c b/src/libstrongswan/crypto/hashers/hasher.c
+index 444a59c5f0c..1aa05ed04bb 100644
+--- a/src/libstrongswan/crypto/hashers/hasher.c
++++ b/src/libstrongswan/crypto/hashers/hasher.c
+@@ -1,6 +1,6 @@
+ /*
+  * Copyright (C) 2012-2015 Tobias Brunner
+- * Copyright (C) 2015-2017 Andreas Steffen
++ * Copyright (C) 2015-2024 Andreas Steffen
+  * Copyright (C) 2005-2006 Martin Willi
+  * Copyright (C) 2005 Jan Hutter
+  *
+@@ -148,6 +148,9 @@ hash_algorithm_t hasher_algorithm_from_oid(int oid)
+ 			return HASH_SHA3_512;
+ 		case OID_ED25519:
+ 		case OID_ED448:
++		case OID_ML_DSA_44:
++		case OID_ML_DSA_65:
++		case OID_ML_DSA_87:
+ 			return HASH_IDENTITY;
+ 		default:
+ 			return HASH_UNKNOWN;
+@@ -462,6 +465,30 @@ int hasher_signature_algorithm_to_oid(hash_algorithm_t alg, key_type_t key)
+ 				default:
+ 					return OID_UNKNOWN;
+ 			}
++		case KEY_ML_DSA_44:
++			switch (alg)
++			{
++				case HASH_IDENTITY:
++					return OID_ML_DSA_44;
++				default:
++					return OID_UNKNOWN;
++			}
++		case KEY_ML_DSA_65:
++			switch (alg)
++			{
++				case HASH_IDENTITY:
++					return OID_ML_DSA_65;
++				default:
++					return OID_UNKNOWN;
++			}
++		case KEY_ML_DSA_87:
++			switch (alg)
++			{
++				case HASH_IDENTITY:
++					return OID_ML_DSA_87;
++				default:
++					return OID_UNKNOWN;
++			}
+ 		default:
+ 			return OID_UNKNOWN;
+ 	}
+@@ -488,6 +515,9 @@ hash_algorithm_t hasher_from_signature_scheme(signature_scheme_t scheme,
+ 			break;
+ 		case SIGN_ED25519:
+ 		case SIGN_ED448:
++		case SIGN_ML_DSA_44:
++		case SIGN_ML_DSA_65:
++		case SIGN_ML_DSA_87:
+ 			return HASH_IDENTITY;
+ 		case SIGN_RSA_EMSA_PKCS1_MD5:
+ 			return HASH_MD5;
+diff --git a/src/libstrongswan/plugins/pem/pem_encoder.c b/src/libstrongswan/plugins/pem/pem_encoder.c
+index 4150d7fa506..f9b7c9aef04 100644
+--- a/src/libstrongswan/plugins/pem/pem_encoder.c
++++ b/src/libstrongswan/plugins/pem/pem_encoder.c
+@@ -41,6 +41,8 @@ bool pem_encoder_encode(cred_encoding_type_t type, chunk_t *encoding,
+ 									&asn1, CRED_PART_END) ||
+ 				cred_encoding_args(args, CRED_PART_ECDSA_PUB_ASN1_DER,
+ 									&asn1, CRED_PART_END) ||
++				cred_encoding_args(args, CRED_PART_PUB_ASN1_DER,
++									&asn1, CRED_PART_END) ||
+ 				cred_encoding_args(args, CRED_PART_EDDSA_PUB_ASN1_DER,
+ 									&asn1, CRED_PART_END))
+ 			{
+@@ -91,7 +93,9 @@ bool pem_encoder_encode(cred_encoding_type_t type, chunk_t *encoding,
+ 				label ="EC PRIVATE KEY";
+ 				break;
+ 			}
+-			if (cred_encoding_args(args, CRED_PART_EDDSA_PRIV_ASN1_DER,
++			if (cred_encoding_args(args, CRED_PART_PRIV_ASN1_DER,
++								   &asn1, CRED_PART_END) ||
++				cred_encoding_args(args, CRED_PART_EDDSA_PRIV_ASN1_DER,
+ 								   &asn1, CRED_PART_END))
+ 			{
+ 				label ="PRIVATE KEY";
+diff --git a/src/libstrongswan/plugins/pem/pem_plugin.c b/src/libstrongswan/plugins/pem/pem_plugin.c
+index d46906168e1..6542f100c96 100644
+--- a/src/libstrongswan/plugins/pem/pem_plugin.c
++++ b/src/libstrongswan/plugins/pem/pem_plugin.c
+@@ -1,5 +1,6 @@
+ /*
+  * Copyright (C) 2009 Martin Willi
++ * Copyright (C) 2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -67,6 +68,15 @@ METHOD(plugin_t, get_features, int,
+ 		PLUGIN_REGISTER(PRIVKEY, pem_private_key_load, FALSE),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ED448),
+ 				PLUGIN_DEPENDS(PRIVKEY, KEY_ED448),
++		PLUGIN_REGISTER(PRIVKEY, pem_private_key_load, FALSE),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ML_DSA_44),
++		PLUGIN_REGISTER(PRIVKEY, pem_private_key_load, FALSE),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ML_DSA_65),
++		PLUGIN_REGISTER(PRIVKEY, pem_private_key_load, FALSE),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ML_DSA_87),
+ 
+ 		/* public key PEM decoding */
+ 		PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
+@@ -87,6 +97,15 @@ METHOD(plugin_t, get_features, int,
+ 		PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_ED448),
+ 				PLUGIN_DEPENDS(PUBKEY, KEY_ED448),
++		PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
++				PLUGIN_DEPENDS(PUBKEY, KEY_ML_DSA_44),
++		PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
++				PLUGIN_DEPENDS(PUBKEY, KEY_ML_DSA_65),
++		PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
++				PLUGIN_DEPENDS(PUBKEY, KEY_ML_DSA_87),
+ 
+ 		/* certificate PEM decoding */
+ 		PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE),
+diff --git a/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c b/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c
+index 15627ab05a5..e0a26833e2c 100644
+--- a/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c
++++ b/src/libstrongswan/plugins/pkcs1/pkcs1_builder.c
+@@ -1,7 +1,7 @@
+ /*
+  * Copyright (C) 2008-2016 Tobias Brunner
+  * Copyright (C) 2008-2009 Martin Willi
+- * Copyright (C) 2000-2008 Andreas Steffen
++ * Copyright (C) 2000-2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -24,94 +24,35 @@
+ #include <asn1/asn1_parser.h>
+ #include <credentials/keys/private_key.h>
+ 
+-/**
+- * ASN.1 definition of a subjectPublicKeyInfo structure
+- */
+-static const asn1Object_t pkinfoObjects[] = {
+-	{ 0, "subjectPublicKeyInfo",ASN1_SEQUENCE,		ASN1_NONE	}, /* 0 */
+-	{ 1,   "algorithm",			ASN1_EOC,			ASN1_RAW	}, /* 1 */
+-	{ 1,   "subjectPublicKey",	ASN1_BIT_STRING,	ASN1_BODY	}, /* 2 */
+-	{ 0, "exit",				ASN1_EOC,			ASN1_EXIT	}
+-};
+-#define PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM	1
+-#define PKINFO_SUBJECT_PUBLIC_KEY			2
+-
+ /**
+  * Load a generic public key from an ASN.1 encoded blob
+  */
+ static public_key_t *parse_public_key(chunk_t blob)
+ {
+-	asn1_parser_t *parser;
+-	chunk_t object;
+-	int objectID;
+-	public_key_t *key = NULL;
+-	key_type_t type = KEY_ANY;
++	chunk_t pubkey;
++	key_type_t type;
+ 
+-	parser = asn1_parser_create(pkinfoObjects, blob);
++	type = public_key_info_decode(blob, &pubkey);
+ 
+-	while (parser->iterate(parser, &objectID, &object))
++	switch (type)
+ 	{
+-		switch (objectID)
+-		{
+-			case PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM:
+-			{
+-				int oid = asn1_parse_algorithmIdentifier(object,
+-										parser->get_level(parser)+1, NULL);
+-
+-				if (oid == OID_RSA_ENCRYPTION || oid == OID_RSAES_OAEP ||
+-					oid == OID_RSASSA_PSS)
+-				{
+-					/* TODO: we should parse parameters for PSS and pass them
+-					 * (and the type), or the complete subjectPublicKeyInfo,
+-					 * along so we can treat these as restrictions when
+-					 * generating signatures with the associated private key */
+-					type = KEY_RSA;
+-				}
+-				else if (oid == OID_EC_PUBLICKEY)
+-				{
+-					/* Need the whole subjectPublicKeyInfo for EC public keys */
+-					key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
+-								KEY_ECDSA, BUILD_BLOB_ASN1_DER, blob, BUILD_END);
+-					goto end;
+-				}
+-				else if (oid == OID_ED25519)
+-				{
+-					/* Need the whole subjectPublicKeyInfo for Ed25519 public keys */
+-					key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
+-								KEY_ED25519, BUILD_BLOB_ASN1_DER, blob, BUILD_END);
+-					goto end;
+-				}
+-				else if (oid == OID_ED448)
+-				{
+-					/* Need the whole subjectPublicKeyInfo for Ed448 public keys */
+-					key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
+-								KEY_ED448, BUILD_BLOB_ASN1_DER, blob, BUILD_END);
+-					goto end;
+-				}
+-				else
+-				{
+-					/* key type not supported */
+-					goto end;
+-				}
+-				break;
+-			}
+-			case PKINFO_SUBJECT_PUBLIC_KEY:
+-				if (object.len > 0 && *object.ptr == 0x00)
+-				{
+-					/* skip initial bit string octet defining 0 unused bits */
+-					object = chunk_skip(object, 1);
+-				}
+-				DBG2(DBG_ASN, "-- > --");
+-				key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type,
+-										 BUILD_BLOB_ASN1_DER, object, BUILD_END);
+-				DBG2(DBG_ASN, "-- < --");
+-				break;
+-		}
++		case KEY_RSA:
++			return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type,
++								BUILD_BLOB_ASN1_DER, pubkey, BUILD_END);
++		case KEY_ECDSA:
++		case KEY_ED25519:
++		case KEY_ED448:
++			/* need the whole subjectPublicKeyInfo for EC public keys */
++			return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type,
++								BUILD_BLOB_ASN1_DER, blob, BUILD_END);
++		case KEY_ML_DSA_44:
++		case KEY_ML_DSA_65:
++		case KEY_ML_DSA_87:
++			return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type,
++								BUILD_BLOB, pubkey, BUILD_END);
++		default:
++			return NULL;
+ 	}
+-
+-end:
+-	parser->destroy(parser);
+-	return key;
+ }
+ 
+ /**
+diff --git a/src/libstrongswan/plugins/pkcs1/pkcs1_plugin.c b/src/libstrongswan/plugins/pkcs1/pkcs1_plugin.c
+index 68a57bceed1..0762a58f75d 100644
+--- a/src/libstrongswan/plugins/pkcs1/pkcs1_plugin.c
++++ b/src/libstrongswan/plugins/pkcs1/pkcs1_plugin.c
+@@ -1,5 +1,6 @@
+ /*
+  * Copyright (C) 2009 Martin Willi
++ * Copyright (C) 2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -55,6 +56,9 @@ METHOD(plugin_t, get_features, int,
+ 				PLUGIN_SDEPEND(PUBKEY, KEY_ECDSA),
+ 				PLUGIN_SDEPEND(PUBKEY, KEY_ED25519),
+ 				PLUGIN_SDEPEND(PUBKEY, KEY_ED448),
++				PLUGIN_SDEPEND(PUBKEY, KEY_ML_DSA_44),
++				PLUGIN_SDEPEND(PUBKEY, KEY_ML_DSA_65),
++				PLUGIN_SDEPEND(PUBKEY, KEY_ML_DSA_87),
+ 				PLUGIN_SDEPEND(PUBKEY, KEY_DSA),
+ 		PLUGIN_REGISTER(PUBKEY, pkcs1_public_key_load, FALSE),
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
+diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
+index defaac19d41..f1d6a2473ed 100644
+--- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
++++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
+@@ -1,5 +1,6 @@
+ /*
+  * Copyright (C) 2012 Tobias Brunner
++ * Copyright (C) 2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -89,6 +90,18 @@ static private_key_t *parse_private_key(chunk_t blob)
+ 						type = KEY_ED448;
+ 						part = BUILD_EDDSA_PRIV_ASN1_DER;
+ 						break;
++					case OID_ML_DSA_44:
++						type = KEY_ML_DSA_44;
++						part = BUILD_BLOB;
++						break;
++					case OID_ML_DSA_65:
++						type = KEY_ML_DSA_65;
++						part = BUILD_BLOB;
++						break;
++					case OID_ML_DSA_87:
++						type = KEY_ML_DSA_87;
++						part = BUILD_BLOB;
++						break;
+ 					default:
+ 						/* key type not supported */
+ 						goto end;
+diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c
+index 44b0b1649bf..60bbd886970 100644
+--- a/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c
++++ b/src/libstrongswan/plugins/pkcs8/pkcs8_plugin.c
+@@ -1,5 +1,6 @@
+ /*
+  * Copyright (C) 2012 Tobias Brunner
++ * Copyright (C) 2024 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -46,9 +47,19 @@ METHOD(plugin_t, get_features, int,
+ 		PLUGIN_REGISTER(PRIVKEY, pkcs8_private_key_load, FALSE),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_RSA),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_RSA),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ECDSA),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ED25519),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ED25519),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ED448),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ED448),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++				PLUGIN_DEPENDS(PRIVKEY, KEY_ML_DSA_87),
+ 	};
+ 	*features = f;
+ 	return countof(f);
+
+diff --git a/src/libstrongswan/plugins/curve25519/curve25519_private_key.c b/src/libstrongswan/plugins/curve25519/curve25519_private_key.c
+index bf5c41486dc..9c4ebec905c 100644
+--- a/src/libstrongswan/plugins/curve25519/curve25519_private_key.c
++++ b/src/libstrongswan/plugins/curve25519/curve25519_private_key.c
+@@ -133,7 +133,7 @@ METHOD(private_key_t, get_public_key, public_key_t*,
+ 	public_key_t *public;
+ 	chunk_t pubkey;
+ 
+-	pubkey = curve25519_public_key_info_encode(this->pubkey);
++	pubkey = public_key_info_encode(this->pubkey, OID_ED25519);
+ 	public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ED25519,
+ 								BUILD_BLOB_ASN1_DER, pubkey, BUILD_END);
+ 	free(pubkey.ptr);
+diff --git a/src/libstrongswan/plugins/curve25519/curve25519_public_key.c b/src/libstrongswan/plugins/curve25519/curve25519_public_key.c
+index 2c20549e984..1f74590591f 100644
+--- a/src/libstrongswan/plugins/curve25519/curve25519_public_key.c
++++ b/src/libstrongswan/plugins/curve25519/curve25519_public_key.c
+@@ -159,7 +159,7 @@ METHOD(public_key_t, get_encoding, bool,
+ {
+ 	bool success = TRUE;
+ 
+-	*encoding = curve25519_public_key_info_encode(this->pubkey);
++	*encoding = public_key_info_encode(this->pubkey, OID_ED25519);
+ 
+ 	if (type != PUBKEY_SPKI_ASN1_DER)
+ 	{
+@@ -325,17 +325,6 @@ curve25519_public_key_t *curve25519_public_key_load(key_type_t type,
+ 	return &this->public;
+ }
+ 
+-/**
+- * See header.
+- */
+-chunk_t curve25519_public_key_info_encode(chunk_t pubkey)
+-{
+-	return asn1_wrap(ASN1_SEQUENCE, "mm",
+-					asn1_wrap(ASN1_SEQUENCE, "m",
+-						asn1_build_known_oid(OID_ED25519)),
+-					asn1_bitstring("c", pubkey));
+-}
+-
+ /**
+  * See header.
+  */
+@@ -351,7 +340,7 @@ bool curve25519_public_key_fingerprint(chunk_t pubkey,
+ 			key = chunk_clone(pubkey);
+ 			break;
+ 		case KEYID_PUBKEY_INFO_SHA1:
+-			key = curve25519_public_key_info_encode(pubkey);
++			key = public_key_info_encode(pubkey, OID_ED25519);
+ 			break;
+ 		default:
+ 			return FALSE;
+
+diff --git a/src/pki/commands/gen.c b/src/pki/commands/gen.c
+index bedde190147..87e674a98cf 100644
+--- a/src/pki/commands/gen.c
++++ b/src/pki/commands/gen.c
+@@ -53,6 +53,18 @@ static int gen()
+ 				{
+ 					type = KEY_ED448;
+ 				}
++				else if (streq(arg, "mldsa44"))
++				{
++					type = KEY_ML_DSA_44;
++				}
++				else if (streq(arg, "mldsa65"))
++				{
++					type = KEY_ML_DSA_65;
++				}
++				else if (streq(arg, "mldsa87"))
++				{
++					type = KEY_ML_DSA_87;
++				}
+ 				else
+ 				{
+ 					return command_usage("invalid key type");
+@@ -95,7 +107,7 @@ static int gen()
+ 		}
+ 		break;
+ 	}
+-	/* default key sizes */
++	/* default values for key types with variable key size */
+ 	if (!size)
+ 	{
+ 		switch (type)
+@@ -106,12 +118,6 @@ static int gen()
+ 			case KEY_ECDSA:
+ 				size = 384;
+ 				break;
+-			case KEY_ED25519:
+-				size = 256;
+-				break;
+-			case KEY_ED448:
+-				size = 456;
+-				break;
+ 			default:
+ 				break;
+ 		}
+@@ -167,7 +173,7 @@ static void __attribute__ ((constructor))reg()
+ {
+ 	command_register((command_t) {
+ 		gen, 'g', "gen", "generate a new private key",
+-		{"[--type rsa|ecdsa|ed25519|ed448] [--size bits] [--safe-primes]",
++		{"[--type rsa|ecdsa|ed25519|ed448|mldsa44|mldsa65|mldsa87] [--size bits] [--safe-primes]",
+ 		 "[--shares n] [--threshold l] [--outform der|pem]"},
+ 		{
+ 			{"help",		'h', 0, "show usage information"},
+diff --git a/src/pki/man/pki---gen.1.in b/src/pki/man/pki---gen.1.in
+index 708f58a52c8..1ea4cede8c4 100644
+--- a/src/pki/man/pki---gen.1.in
++++ b/src/pki/man/pki---gen.1.in
+@@ -46,7 +46,8 @@ Read command line options from \fIfile\fR.
+ .TP
+ .BI "\-t, \-\-type " type
+ Type of key to generate. Either \fIrsa\fR, \fIecdsa\fR, \fIed25519\fR,
+-or \fIed448\fR, defaults to \fIrsa\fR.
++\fIed448\fR, \fImldsa44\fR, \fImldsa65\fR or \fImldsa87\fR, defaults to
++\fIrsa\fR.
+ .TP
+ .BI "\-s, \-\-size " bits
+ Key length in bits. Defaults to 2048 for \fIrsa\fR and 384 for \fIecdsa\fR.
+
+diff --git a/src/libstrongswan/plugins/wolfssl/Makefile.am b/src/libstrongswan/plugins/wolfssl/Makefile.am
+index a0fee2b4913..56d350808dc 100644
+--- a/src/libstrongswan/plugins/wolfssl/Makefile.am
++++ b/src/libstrongswan/plugins/wolfssl/Makefile.am
+@@ -22,6 +22,8 @@ libstrongswan_wolfssl_la_SOURCES = \
+ 	wolfssl_ec_public_key.h wolfssl_ec_public_key.c \
+ 	wolfssl_ed_private_key.h wolfssl_ed_private_key.c \
+ 	wolfssl_ed_public_key.h wolfssl_ed_public_key.c \
++	wolfssl_ml_dsa_private_key.h wolfssl_ml_dsa_private_key.c \
++	wolfssl_ml_dsa_public_key.h wolfssl_ml_dsa_public_key.c \
+ 	wolfssl_hasher.h wolfssl_hasher.c \
+ 	wolfssl_hmac.h wolfssl_hmac.c \
+ 	wolfssl_kdf.h wolfssl_kdf.c \
+diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_private_key.c b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_private_key.c
+new file mode 100644
+index 00000000000..69e5816f8db
+--- /dev/null
++++ b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_private_key.c
+@@ -0,0 +1,413 @@
++/*
++ * Copyright (C) 2024-2025 Andreas Steffen, strongSec GmbH
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "wolfssl_common.h"
++
++#if defined(WOLFSSL_HAVE_MLDSA)
++
++#include "wolfssl_ml_dsa_private_key.h"
++
++#include <utils/debug.h>
++#include <asn1/asn1.h>
++#include <credentials/cred_encoding.h>
++#include <credentials/keys/public_key.h>
++#include <credentials/keys/signature_params.h>
++
++#include <wolfssl/wolfcrypt/asn.h>
++#include <wolfssl/wolfcrypt/wc_mldsa.h>
++
++typedef struct private_private_key_t private_private_key_t;
++
++/**
++ * Private data
++ */
++struct private_private_key_t {
++
++	/**
++	 * Public interface
++	 */
++	private_key_t public;
++
++	/**
++	 * Key object
++	 */
++	wc_MlDsaKey key;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Secret key seed
++	 */
++	chunk_t keyseed;
++
++	/**
++	 * Reference count
++	 */
++	refcount_t ref;
++};
++
++/* from wolfss_ml_dsa_public_key.c */
++bool wolfssl_ml_dsa_enabled(key_type_t type, uint8_t *level);
++bool wolfssl_ml_dsa_fingerprint(MlDsaKey *key, key_type_t type,
++								cred_encoding_type_t enc_type, chunk_t *fp);
++
++METHOD(private_key_t, sign, bool,
++	private_private_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t *signature)
++{
++	pqc_params_t pqc_params;
++	u_int sig_len;
++	int ret;
++
++	if (key_type_from_signature_scheme(scheme) != this->type)
++	{
++		DBG1(DBG_LIB, "signature scheme %N not supported",
++					   signature_scheme_names, scheme);
++		return FALSE;
++	}
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		return FALSE;
++	}
++
++	sig_len = wc_MlDsaKey_SigSize(&this->key);
++	*signature = chunk_alloc(sig_len);
++
++	/* deterministic or randomized signature? */
++	if 	(pqc_params.deterministic)
++	{
++		uint8_t seed[MLDSA_RND_SZ];
++
++		memset(seed, 0x00, MLDSA_RND_SZ);
++		ret = wc_MlDsaKey_SignCtxWithSeed(&this->key, pqc_params.ctx.ptr,
++								pqc_params.ctx.len, signature->ptr, &sig_len,
++								data.ptr, data.len, seed);
++	}
++	else
++	{
++		WC_RNG rng;
++
++		if (wc_InitRng(&rng) != 0)
++		{
++			DBG1(DBG_LIB, "initializing random generator failed");
++			pqc_params_free(&pqc_params);
++			chunk_free(signature);
++			return FALSE;
++		}
++		ret = wc_MlDsaKey_SignCtx(&this->key, pqc_params.ctx.ptr,
++								pqc_params.ctx.len, signature->ptr, &sig_len,
++								data.ptr, data.len, &rng);
++		wc_FreeRng(&rng);
++	}
++	pqc_params_free(&pqc_params);
++
++	if (ret != 0)
++	{
++		chunk_free(signature);
++		return FALSE;
++	}
++	return TRUE;
++}
++
++METHOD(private_key_t, decrypt, bool,
++	private_private_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "ML-DSA private key decryption not implemented");
++	return FALSE;
++}
++
++METHOD(private_key_t, get_keysize, int,
++	private_private_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++METHOD(private_key_t, get_type, key_type_t,
++	private_private_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(private_key_t, get_public_key, public_key_t*,
++	private_private_key_t *this)
++{
++	public_key_t *public_key;
++	chunk_t pubkey;
++	int len;
++
++	len = get_public_key_size(this->type);
++	pubkey = chunk_alloc(len);
++
++	if (wc_MlDsaKey_ExportPubRaw(&this->key, pubkey.ptr, &len) != 0)
++	{
++		chunk_free(&pubkey);
++		return NULL;
++	}
++
++	public_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, this->type,
++									BUILD_BLOB, pubkey, BUILD_END);
++	chunk_free(&pubkey);
++
++	return public_key;
++}
++
++METHOD(private_key_t, get_fingerprint, bool,
++	private_private_key_t *this, cred_encoding_type_t type,	chunk_t *fp)
++{
++	bool success;
++
++	if (lib->encoding->get_cache(lib->encoding, type, this, fp))
++	{
++		return TRUE;
++	}
++
++	success = wolfssl_ml_dsa_fingerprint(&this->key, this->type, type, fp);
++	if (success)
++	{
++		lib->encoding->cache(lib->encoding, type, this, fp);
++	}
++
++	return success;
++}
++
++METHOD(private_key_t, get_encoding, bool,
++	private_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding)
++{
++	switch (type)
++	{
++		case PRIVKEY_ASN1_DER:
++		case PRIVKEY_PEM:
++		{
++			bool success = TRUE;
++			int oid = key_type_to_oid(this->type);
++
++			*encoding = asn1_wrap(ASN1_SEQUENCE, "cmm",
++							ASN1_INTEGER_0,
++							asn1_algorithmIdentifier(oid),
++							asn1_wrap(ASN1_OCTET_STRING, "m",
++								asn1_simple_object(ASN1_CONTEXT_S_0,
++												   this->keyseed))
++						);
++			if (type == PRIVKEY_PEM)
++			{
++				chunk_t asn1_encoding = *encoding;
++
++				success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM,
++								NULL, encoding, CRED_PART_PRIV_ASN1_DER,
++								asn1_encoding, CRED_PART_END);
++				chunk_clear(&asn1_encoding);
++			}
++
++			return success;
++		}
++		default:
++			return FALSE;
++	}
++}
++
++METHOD(private_key_t, get_ref, private_key_t*,
++	private_private_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(private_key_t, destroy, void,
++	private_private_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this);
++		wc_MlDsaKey_Free(&this->key);
++		chunk_clear(&this->keyseed);
++		free(this);
++	}
++}
++
++/**
++ * Generic private constructor
++ */
++static private_private_key_t *create_instance(key_type_t type, uint8_t level,
++											  chunk_t keyseed)
++{
++	private_private_key_t *this;
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.sign = _sign,
++			.decrypt = _decrypt,
++			.get_keysize = _get_keysize,
++			.get_public_key = _get_public_key,
++			.equals = private_key_equals,
++			.belongs_to = private_key_belongs_to,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = private_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.keyseed = keyseed,
++		.ref = 1,
++	);
++
++	if (wc_MlDsaKey_Init(&this->key, NULL, INVALID_DEVID) != 0 ||
++		wc_MlDsaKey_SetParams(&this->key, level) != 0)
++	{
++		destroy(this);
++		return NULL;
++	}
++
++	/* derive private and public key from seed */
++	if (wc_MlDsaKey_MakeKeyFromSeed(&this->key, keyseed.ptr) != 0)
++	{
++		DBG1(DBG_LIB, "deriving %N from seed failed", key_type_names, type);
++		destroy(this);
++		return NULL;
++	}
++
++	return this;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *wolfssl_ml_dsa_private_key_gen(key_type_t type, va_list args)
++{
++	private_private_key_t *this;
++	WC_RNG rng;
++	chunk_t seed;
++	uint8_t level = 0;
++	int ret;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_KEY_SIZE:
++				/* just ignore the key size */
++				va_arg(args, u_int);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (!wolfssl_ml_dsa_enabled(type, &level))
++	{
++		return NULL;
++	}
++
++	if (wc_InitRng(&rng) != 0)
++	{
++		DBG1(DBG_LIB, "initializing random generator failed");
++		return NULL;
++	}
++
++	seed = chunk_alloc(MLDSA_SEED_SZ);
++	ret = wc_RNG_GenerateBlock(&rng, seed.ptr, seed.len);
++	wc_FreeRng(&rng);
++
++	if (ret != 0)
++	{
++		DBG1(DBG_LIB, "generating random seed failed");
++		chunk_free(&seed);
++		return NULL;
++	}
++
++	this = create_instance(type, level, seed);
++	if (!this)
++	{
++		return NULL;
++	}
++
++	return &this->public;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *wolfssl_ml_dsa_private_key_load(key_type_t type, va_list args)
++{
++	private_private_key_t *this;
++	chunk_t priv = chunk_empty;
++	uint8_t level = 0;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				priv = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (priv.len == 0 || !wolfssl_ml_dsa_enabled(type, &level))
++	{
++		return NULL;
++	}
++
++	/* unwrap seed-only format */
++	if (priv.len == MLDSA_SEED_SZ + 2 &&
++		priv.ptr[0] == ASN1_CONTEXT_S_0 && priv.ptr[1] == MLDSA_SEED_SZ)
++	{
++		priv = chunk_skip(priv, 2);
++	}
++	else if (priv.len > MLDSA_SEED_SZ)
++	{
++		/* if extracting the seed from the presumed "both" format fails, just
++		 * return, we currently don't support the expanded format */
++		if (priv.ptr[0] != ASN1_SEQUENCE ||
++			asn1_unwrap(&priv, &priv) != ASN1_SEQUENCE ||
++			asn1_unwrap(&priv, &priv) != ASN1_OCTET_STRING ||
++			priv.len != MLDSA_SEED_SZ)
++		{
++			return NULL;
++		}
++	}
++	if (priv.len != MLDSA_SEED_SZ)
++	{
++		DBG1(DBG_LIB, "error: the size of the loaded ML-DSA private key seed "
++			 "is %u bytes instead of %d bytes", priv.len, MLDSA_SEED_SZ);
++
++		return NULL;
++	}
++
++	this = create_instance(type, level, chunk_clone(priv));
++	if (!this)
++	{
++		return NULL;
++	}
++
++	return &this->public;
++}
++#endif /* WOLFSSL_HAVE_MLDSA */
+diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_private_key.h b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_private_key.h
+new file mode 100644
+index 00000000000..6f9ab1f0695
+--- /dev/null
++++ b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_private_key.h
+@@ -0,0 +1,48 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen, strongSec GmbH
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup wolfssl_ml_dsa_private_key wolfssl_ml_dsa_private_key
++ * @{ @ingroup wolfssl_p
++ */
++
++#ifndef WOLFSSL_PLUGIN_ML_DSA_PRIVATE_KEY_H_
++#define WOLFSSL_PLUGIN_ML_DSA_PRIVATE_KEY_H_
++
++#include <credentials/builder.h>
++#include <credentials/keys/private_key.h>
++
++/**
++ * Generate an ML-DSA private key using wolfSSL.
++ *
++ * @param type		key type, must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			generated key, NULL on failure
++ */
++private_key_t *wolfssl_ml_dsa_private_key_gen(key_type_t type, va_list args);
++
++/**
++ * Load an ML-DSA private key using wolfSSL.
++ *
++ * Accepts a BUILD_BLOB_ASN1_DER argument.
++ *
++ * @param type		key type, must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++private_key_t *wolfssl_ml_dsa_private_key_load(key_type_t type, va_list args);
++
++#endif /** WOLFSSL_PLUGIN_ML_DSA_PRIVATE_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_public_key.c b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_public_key.c
+new file mode 100644
+index 00000000000..0393512bf98
+--- /dev/null
++++ b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_public_key.c
+@@ -0,0 +1,341 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen, strongSec GmbH
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "wolfssl_common.h"
++
++#if defined(WOLFSSL_HAVE_MLDSA)
++
++#include "wolfssl_ml_dsa_public_key.h"
++
++#include <utils/debug.h>
++#include <asn1/asn1.h>
++#include <credentials/keys/signature_params.h>
++
++#include <wolfssl/wolfcrypt/asn.h>
++#include <wolfssl/wolfcrypt/wc_mldsa.h>
++
++typedef struct private_public_key_t private_public_key_t;
++
++/**
++ * Private data
++ */
++struct private_public_key_t {
++
++	/**
++	 * Public interface
++	 */
++	public_key_t public;
++
++	/**
++	 * Key object
++	 */
++	wc_MlDsaKey key;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Reference count
++	 */
++	refcount_t ref;
++};
++
++METHOD(public_key_t, get_type, key_type_t,
++	private_public_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(public_key_t, verify, bool,
++	private_public_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t signature)
++{
++	pqc_params_t pqc_params;
++	int ret, result;
++
++	if (key_type_from_signature_scheme(scheme) != this->type)
++	{
++		DBG1(DBG_LIB, "signature scheme %N not supported",
++					   signature_scheme_names, scheme);
++		return FALSE;
++	}
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		return FALSE;
++	}
++
++	ret = wc_MlDsaKey_VerifyCtx(&this->key, signature.ptr, signature.len,
++								pqc_params.ctx.ptr, pqc_params.ctx.len,
++								data.ptr, data.len, &result);
++	pqc_params_free(&pqc_params);
++
++	return (ret == 0) && (result == 1);
++}
++
++METHOD(public_key_t, encrypt_, bool,
++	private_public_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "encryption scheme %N not supported", encryption_scheme_names,
++		 scheme);
++	return FALSE;
++}
++
++METHOD(public_key_t, get_keysize, int,
++	private_public_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++/**
++ * Generate two types of ML-DSA fingerprints
++ */
++bool wolfssl_ml_dsa_fingerprint(MlDsaKey *key, key_type_t type,
++								cred_encoding_type_t enc_type, chunk_t *fp)
++{
++	chunk_t pubkey = chunk_empty, encoding = chunk_empty;
++	hasher_t *hasher;
++	int len;
++	bool success = FALSE;
++
++	*fp = chunk_empty;
++	len = get_public_key_size(type);
++	pubkey = chunk_alloc(len);
++
++	if (wc_MlDsaKey_ExportPubRaw(key, pubkey.ptr, &len) != 0)
++	{
++		goto end;
++	}
++
++	switch (enc_type)
++	{
++		case KEYID_PUBKEY_SHA1:
++			encoding = chunk_clone(pubkey);
++			break;
++		case KEYID_PUBKEY_INFO_SHA1:
++			encoding = public_key_info_encode(pubkey, key_type_to_oid(type));
++			break;
++		default:
++			goto end;
++	}
++
++	hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
++	if (!hasher || !hasher->allocate_hash(hasher, encoding, fp))
++	{
++		DBG1(DBG_LIB, "SHA1 hash algorithm not supported");
++		DESTROY_IF(hasher);
++		goto end;
++	}
++	hasher->destroy(hasher);
++	success = TRUE;
++
++end:
++	chunk_free(&pubkey);
++	chunk_free(&encoding);
++
++	return success;
++}
++
++METHOD(public_key_t, get_fingerprint, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *fp)
++{
++	bool success;
++
++	if (lib->encoding->get_cache(lib->encoding, type, this, fp))
++	{
++		return TRUE;
++	}
++
++	success = wolfssl_ml_dsa_fingerprint(&this->key, this->type, type, fp);
++	if (success)
++	{
++		lib->encoding->cache(lib->encoding, type, this, fp);
++	}
++
++	return success;
++}
++
++
++METHOD(public_key_t, get_encoding, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding)
++{
++	bool success = FALSE;
++	chunk_t pubkey;
++	int len, oid;
++
++	len = get_public_key_size(this->type);
++	pubkey = chunk_alloc(len);
++
++	if (wc_MlDsaKey_ExportPubRaw(&this->key, pubkey.ptr, &len) != 0)
++	{
++		*encoding = chunk_empty;
++		goto end;
++	}
++
++	oid = key_type_to_oid(this->type);
++	*encoding = public_key_info_encode(pubkey, oid);
++	success = TRUE;
++
++	if (type != PUBKEY_SPKI_ASN1_DER)
++	{
++		chunk_t asn1_encoding = *encoding;
++
++		success = lib->encoding->encode(lib->encoding, type,
++						NULL, encoding, CRED_PART_PUB_ASN1_DER,
++						asn1_encoding, CRED_PART_END);
++		chunk_clear(&asn1_encoding);
++	}
++
++end:
++	chunk_free(&pubkey);
++
++	return success;
++}
++
++METHOD(public_key_t, get_ref, public_key_t*,
++	private_public_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(public_key_t, destroy, void,
++	private_public_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this);
++		wc_MlDsaKey_Free(&this->key);
++		free(this);
++	}
++}
++
++/**
++ * Checks if a given ML-DSA type is enabled and sets some parameters
++ */
++bool wolfssl_ml_dsa_enabled(key_type_t type, uint8_t *level)
++{
++	*level = 0;
++
++	if (type == KEY_ML_DSA_44)
++	{
++#ifndef WOLFSSL_NO_ML_DSA_44
++		*level = WC_ML_DSA_44;
++#endif
++	}
++	else if (type == KEY_ML_DSA_65)
++	{
++#ifndef WOLFSSL_NO_ML_DSA_65
++		*level = WC_ML_DSA_65;
++#endif
++	}
++	else if (type == KEY_ML_DSA_87)
++	{
++#ifndef WOLFSSL_NO_ML_DSA_87
++		*level = WC_ML_DSA_87;
++#endif
++	}
++
++	return *level != 0;
++}
++
++/**
++ * Generic private constructor
++ */
++static private_public_key_t *create_empty(key_type_t type, uint8_t level)
++{
++	private_public_key_t *this;
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.verify = _verify,
++			.encrypt = _encrypt_,
++			.get_keysize = _get_keysize,
++			.equals = public_key_equals,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = public_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.ref = 1,
++	);
++
++	if (wc_MlDsaKey_Init(&this->key, NULL, INVALID_DEVID) != 0 ||
++	   (level && wc_MlDsaKey_SetParams(&this->key, level) != 0))
++	{
++		free(this);
++		return NULL;
++	}
++
++	return this;
++}
++
++/*
++ * Described in header
++ */
++public_key_t *wolfssl_ml_dsa_public_key_load(key_type_t type, va_list args)
++{
++	private_public_key_t *this;
++	chunk_t pkcs1, blob = chunk_empty;
++	uint8_t level = 0;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				blob = va_arg(args, chunk_t);
++				continue;
++			case BUILD_BLOB_ASN1_DER:
++				pkcs1 = va_arg(args, chunk_t);
++				type = public_key_info_decode(pkcs1, &blob);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (!wolfssl_ml_dsa_enabled(type, &level) || blob.len == 0)
++	{
++		return NULL;
++	}
++
++	this = create_empty(type, level);
++	if (!this)
++	{
++		return NULL;
++	}
++
++	if (wc_MlDsaKey_ImportPubRaw(&this->key, blob.ptr, blob.len) != 0)
++	{
++		DBG1(DBG_LIB, "importing ML-DSA public key failed");
++		destroy(this);
++		return NULL;
++	}
++
++	return &this->public;
++}
++#endif /* WOLFSSL_HAVE_MLDSA */
+diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_public_key.h b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_public_key.h
+new file mode 100644
+index 00000000000..c3f34dad5f8
+--- /dev/null
++++ b/src/libstrongswan/plugins/wolfssl/wolfssl_ml_dsa_public_key.h
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen, strongSec GmbH
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup wolfssl_ml_dsa_public_key wolfssl_ml_dsa_public_key
++ * @{ @ingroup wolfssl_p
++ */
++
++#ifndef WOLFSSL_PLUGIN_ML_DSA_PUBLIC_KEY_H_
++#define WOLFSSL_PLUGIN_ML_DSA_PUBLIC_KEY_H_
++
++#include <credentials/builder.h>
++#include <credentials/keys/public_key.h>
++
++/**
++ * Load an ML-DSA public key using wolfSSL.
++ *
++ * Accepts a BUILD_BLOB_ASN1_DER argument.
++ *
++ * @param type		key type, must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++public_key_t *wolfssl_ml_dsa_public_key_load(key_type_t type, va_list args);
++
++#endif /** WOLFSSL_PLUGIN_ML_DSA_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c b/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c
+index 73b73780c54..877a647b112 100644
+--- a/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c
++++ b/src/libstrongswan/plugins/wolfssl/wolfssl_plugin.c
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (C) 2021 Andreas Steffen, strongSec GmbH
++ * Copyright (C) 2021-2024 Andreas Steffen, strongSec GmbH
+  * Copyright (C) 2019-2024 Tobias Brunner, codelabs GmbH
+  * Copyright (C) 2019 Sean Parkinson, wolfSSL Inc.
+  *
+@@ -36,6 +36,8 @@
+ #include "wolfssl_ec_public_key.h"
+ #include "wolfssl_ed_private_key.h"
+ #include "wolfssl_ed_public_key.h"
++#include "wolfssl_ml_dsa_private_key.h"
++#include "wolfssl_ml_dsa_public_key.h"
+ #include "wolfssl_hasher.h"
+ #include "wolfssl_hmac.h"
+ #include "wolfssl_kdf.h"
+@@ -507,6 +509,62 @@ METHOD(plugin_t, get_features, int,
+ 		PLUGIN_REGISTER(HASHER, return_null),
+ 			PLUGIN_PROVIDE(HASHER, HASH_IDENTITY),
+ #endif /* HAVE_ED25519 || HAVE_ED448 */
++#if defined(WOLFSSL_HAVE_MLDSA)
++		/* ML-DSA public key loading */
++		PLUGIN_REGISTER(PUBKEY, wolfssl_ml_dsa_public_key_load, TRUE),
++	#ifndef WOLFSSL_NO_ML_DSA_44
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_65
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_87
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
++	#endif
++			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
++		/* ML-DSA private key loading */
++		PLUGIN_REGISTER(PRIVKEY, wolfssl_ml_dsa_private_key_load, TRUE),
++	#ifndef WOLFSSL_NO_ML_DSA_44
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_65
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_87
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++	#endif
++		/* ML-DSA private key generation */
++		PLUGIN_REGISTER(PRIVKEY_GEN, wolfssl_ml_dsa_private_key_gen, FALSE),
++	#ifndef WOLFSSL_NO_ML_DSA_44
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_44),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_65
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_65),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_87
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_87),
++	#endif
++		/* ML-DSA signature schemes*/
++	#ifndef WOLFSSL_NO_ML_DSA_44
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_44),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_65
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_65),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_87
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_87),
++	#endif
++		/* ML-DSA signature verification schemes */
++	#ifndef WOLFSSL_NO_ML_DSA_44
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_44),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_65
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_65),
++	#endif
++	#ifndef WOLFSSL_NO_ML_DSA_87
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_87),
++	#endif
++#endif /* WOLFSSL_HAVE_MLDSA */
+ #ifndef WC_NO_RNG
+ 		/* generic key loader */
+ 		PLUGIN_REGISTER(RNG, wolfssl_rng_create),
+diff --git a/testing/scripts/recipes/012_wolfssl.mk b/testing/scripts/recipes/012_wolfssl.mk
+index e553552c646..be5fef14dcf 100644
+--- a/testing/scripts/recipes/012_wolfssl.mk
++++ b/testing/scripts/recipes/012_wolfssl.mk
+@@ -26,6 +26,7 @@ CONFIG_OPTS = \
+ 	--enable-ed25519 \
+ 	--enable-ed448 \
+ 	--enable-keygen \
++	--enable-mldsa \
+ 	--enable-mlkem \
+ 	--enable-rsapss \
+ 	--enable-sha3 \
+
+diff --git a/scripts/Makefile.am b/scripts/Makefile.am
+index 0548f661ea9..76f5bc943b7 100644
+--- a/scripts/Makefile.am
++++ b/scripts/Makefile.am
+@@ -7,7 +7,8 @@ AM_CPPFLAGS = \
+ 
+ noinst_PROGRAMS = bin2array bin2sql id2sql key2keyid keyid2sql oid2der \
+ 	thread_analysis dh_speed pubkey_speed crypt_burn hash_burn fetch \
+-	dnssec malloc_speed aes-test settings-test timeattack nist_kem_kat
++	dnssec malloc_speed aes-test settings-test timeattack nist_kem_kat \
++	nist_acvp
+ 
+ if USE_TLS
+   noinst_PROGRAMS += tls_test
+@@ -32,6 +33,7 @@ fetch_SOURCES = fetch.c
+ dnssec_SOURCES = dnssec.c
+ timeattack_SOURCES = timeattack.c
+ nist_kem_kat_SOURCES = nist_kem_kat.c
++nist_acvp_SOURCES = nist_acvp.c
+ 
+ id2sql_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+ key2keyid_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+@@ -48,6 +50,7 @@ aes_test_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+ settings_test_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+ timeattack_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la $(RTLIB)
+ nist_kem_kat_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
++nist_acvp_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la
+ 
+ if USE_IMCV
+   AM_CPPFLAGS += -I$(top_srcdir)/src/libimcv
+diff --git a/scripts/nist_acvp.c b/scripts/nist_acvp.c
+new file mode 100644
+index 00000000000..cba04bbfa31
+--- /dev/null
++++ b/scripts/nist_acvp.c
+@@ -0,0 +1,183 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++#include <unistd.h>
++#include <getopt.h>
++#include <errno.h>
++
++#include <library.h>
++
++static void usage(FILE *out, char *name)
++{
++	fprintf(out, "Convert NIST ACVP JSON entries into struct\n");
++	fprintf(out, "%s [OPTIONS]\n\n", name);
++	fprintf(out, "Options:\n");
++	fprintf(out, "  -h, --help          print this help.\n");
++	fprintf(out, "  -i, --in=FILE       input file file (default STDIN).\n");
++	fprintf(out, "  -o, --out=FILE      output file (default STDOUT).\n");
++	fprintf(out, "\n");
++}
++
++int main(int argc, char *argv[])
++{
++	FILE *in = stdin;
++	FILE *out = stdout;
++	char line[90000], *pos, *eol, *param, *value;
++	size_t param_len, value_len;
++	int n = 0;
++
++	library_init(NULL, "nist-kem-kat");
++	atexit(library_deinit);
++
++	while (true)
++	{
++		struct option long_opts[] = {
++			{"help",	no_argument,		NULL,	'h' },
++			{"in",		required_argument,	NULL,	'i' },
++			{"out",		required_argument,	NULL,	'o' },
++			{0,0,0,0 },
++		};
++		switch (getopt_long(argc, argv, "h:m:c:i:o:", long_opts, NULL))
++		{
++			case EOF:
++				break;
++			case 'h':
++				usage(stdout, argv[0]);
++				return 0;
++			case 'i':
++				in = fopen(optarg, "r");
++				if (!in)
++				{
++					fprintf(stderr, "failed to open '%s': %s\n", optarg,
++							strerror(errno));
++					usage(stderr, argv[0]);
++					return 1;
++				}
++				continue;
++			case 'o':
++				out = fopen(optarg, "w");
++				if (!out)
++				{
++					fprintf(stderr, "failed to open '%s': %s\n", optarg,
++							strerror(errno));
++					usage(stderr, argv[0]);
++					return 1;
++				}
++				continue;
++			default:
++				usage(stderr, argv[0]);
++				return 1;
++		}
++		break;
++	}
++
++	while (fgets(line, sizeof(line), in))
++	{
++		pos = strchr(line, ':');
++		if (!pos)
++		{
++			continue;
++		}
++		value = pos + 1;
++
++		/* determine end of line */
++		eol = strchr(value, '\n');
++		if (!eol)
++		{
++			fprintf(stderr, "eol not found\n");
++			break;
++		}
++		value_len = eol - value;
++
++		while (value_len && *value == ' ')
++		{
++			value++;
++			value_len--;
++		}
++
++		/* remove optional comma trailing the value */
++		if (value_len && value[value_len-1] == ',')
++		{
++			value_len--;
++		}
++
++		if (value_len < 2 || *value != '"' || value[value_len-1] != '"')
++		{
++			fprintf(stderr, "no double quotes around value found\n");
++			break;
++		}
++		value++;
++		value_len -= 2;
++
++		param = line;
++		param_len = pos - line;
++
++		/* remove preceding whitespace from param */
++		while (param_len && *param == ' ')
++		{
++			param++;
++			param_len--;
++		}
++
++		/* remove double quotes from param */
++		if (param_len < 2 || *param != '"' || param[param_len - 1] != '"')
++		{
++			fprintf(stderr, "no double quotes around parameter found\n");
++			break;
++		}
++		param++;
++		param_len -= 2;
++		param[param_len] = '\0';
++
++		fprintf(out, "%s:\n", param);
++  		fprintf(out, "\t  chunk_from_chars(");
++
++  		n = 0;
++		while (value_len > 1)
++		{
++			if (n > 0)
++			{
++				fprintf(out, ",");
++				if (n % 80 == 0)
++				{
++					fprintf(out, " /* %4d */", n);
++				}
++			}
++			if (n % 16 == 0)
++			{
++				fprintf(out, "\n\t\t");
++			}
++			fprintf(out, "0x%c%c", tolower(value[0]), tolower(value[1]));
++			value += 2;
++			value_len -= 2;
++			n++;
++		}
++		fprintf(out, "),/* %4d */\n", n);
++	}
++	if (in != stdin)
++	{
++		fclose(in);
++	}
++	if (out != stdout)
++	{
++		fclose(out);
++	}
++	return 0;
++}
+
+diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am
+index e04a793e473..0f79947ae83 100644
+--- a/src/libstrongswan/tests/Makefile.am
++++ b/src/libstrongswan/tests/Makefile.am
+@@ -66,6 +66,7 @@ libstrongswan_tests_SOURCES = tests.h tests.c \
+   suites/test_prf_plus.c \
+   suites/test_ed25519.c \
+   suites/test_ed448.c \
++  suites/test_ml_dsa.c \
+   suites/test_signature_params.c \
+   suites/test_metadata.c \
+   suites/test_metadata_set.c
+diff --git a/src/libstrongswan/tests/suites/test_ml_dsa.c b/src/libstrongswan/tests/suites/test_ml_dsa.c
+new file mode 100644
+index 00000000000..f4920aa2a6e
+--- /dev/null
++++ b/src/libstrongswan/tests/suites/test_ml_dsa.c
+@@ -0,0 +1,2069 @@
++/*
++ * Copyright (C) 2024-2025 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "test_suite.h"
++
++#include <time.h>
++
++typedef struct test_t test_t;
++
++struct test_t {
++	key_type_t type;
++	u_int k;
++	u_int omega;
++	chunk_t asn1_seed;
++	chunk_t seed;
++	chunk_t asn1_pubkey;
++	chunk_t pubkey;
++	chunk_t sig;
++	chunk_t sig_ctx;
++};
++
++/**
++ * ACVP FIPS 204 ML-DSA keyGen test vectors (1, 26, 51)
++ */
++static test_t tests[] = {
++	{ KEY_ML_DSA_44, 4, 80,
++	  chunk_from_chars(
++		0x30,0x34,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,
++		0x03,0x11,0x04,0x22,0x80,0x20),
++	  chunk_from_chars(
++		0x93,0xef,0x2e,0x6e,0xf1,0xfb,0x08,0x99,0x9d,0x14,0x2a,0xbe,0x02,0x95,0x48,0x23,
++		0x70,0xd3,0xf4,0x3b,0xdb,0x25,0x4a,0x78,0xe2,0xb0,0xd5,0x16,0x8e,0xca,0x06,0x5f),/*   32 */
++	  chunk_from_chars(
++		0x30,0x82,0x05,0x32,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,
++		0x11,0x03,0x82,0x05,0x21,0x00),
++	  chunk_from_chars(
++		0xbc,0x5f,0xf8,0x10,0xeb,0x08,0x90,0x48,0xb8,0xab,0x30,0x20,0xa7,0xbd,0x3b,0x16,
++		0xc0,0xe0,0xca,0x3d,0x6b,0x97,0xe4,0x64,0x6c,0x2c,0xca,0xe0,0xbb,0xf1,0x9e,0xf7,
++		0x23,0x0a,0x19,0xd7,0x5a,0xdb,0xde,0xd5,0x2d,0xb8,0x55,0xe2,0x52,0xa7,0x19,0xfc,
++		0xbd,0x14,0x7b,0xa6,0x7b,0x2f,0xad,0x14,0xed,0x0e,0x68,0xfd,0xfe,0x8c,0x65,0xba,
++		0xde,0xac,0xb0,0x91,0x11,0x93,0xad,0xfa,0x87,0x94,0xd7,0x8f,0x8e,0x3d,0x66,0x2a, /*   80 */
++		0x1c,0x49,0xda,0x81,0x9f,0xd9,0x59,0xe7,0xf0,0x78,0xf2,0x03,0xc4,0x56,0xf8,0xb6,
++		0xe7,0xc9,0x41,0x58,0x98,0xe5,0x41,0xc7,0x30,0x32,0xdb,0xd6,0x19,0xea,0xf6,0x0f,
++		0x8d,0x64,0xf8,0x68,0x3d,0xa9,0x9e,0xca,0x51,0x22,0x0b,0x0a,0xca,0x28,0x46,0x40,
++		0x99,0xf5,0x47,0xc0,0x27,0x77,0xbd,0x37,0xd8,0x4a,0x59,0xbd,0x37,0xed,0x7a,0x8a,
++		0x92,0x63,0x3c,0x75,0xd0,0x7c,0x79,0x3f,0xe7,0x25,0x2b,0x58,0x4a,0xbf,0x6a,0x15, /*  160 */
++		0xee,0x14,0x50,0x7e,0x5e,0x19,0x3f,0x89,0x86,0x4d,0x09,0xac,0x87,0x27,0xa6,0xd0,
++		0x42,0x1f,0x0c,0x19,0xf0,0xe2,0xfb,0xfc,0x21,0x3d,0x3f,0xbd,0x70,0xf4,0xf9,0x76,
++		0x2c,0xec,0xff,0x23,0x1e,0x9c,0x8a,0x76,0x28,0xd3,0xf8,0xb0,0x85,0x7b,0x03,0x2d,
++		0x32,0xde,0x62,0xff,0x8e,0xcb,0xf4,0x00,0x82,0x89,0xbf,0x34,0x40,0x36,0x65,0xf8,
++		0x1a,0x08,0x1a,0xd5,0xa8,0x5a,0x28,0x2f,0x99,0xba,0xb9,0xe5,0x38,0x5a,0xfb,0xcc, /*  240 */
++		0xcf,0x44,0xb7,0x4c,0x01,0x96,0xc7,0x54,0x55,0x27,0xec,0x30,0x26,0xda,0x12,0x80,
++		0xc4,0xeb,0x37,0xd0,0x9c,0xfe,0x3e,0xc4,0xb4,0x91,0x0b,0x62,0xeb,0x98,0x15,0xa4,
++		0x25,0xc6,0x59,0x0f,0xc4,0xad,0x3f,0xbb,0x22,0x57,0x52,0xcc,0x1f,0xc5,0x69,0x3f,
++		0x18,0x7e,0x7d,0xec,0x4e,0xef,0xbe,0xb6,0xb9,0x1b,0xd9,0x1c,0x5e,0x2e,0xa6,0xa9,
++		0x1d,0x14,0xd0,0x97,0xbe,0x20,0x3f,0xba,0x0b,0xf9,0x37,0xc9,0x75,0x07,0xdc,0x00, /*  320 */
++		0x7c,0x4c,0xaa,0x9b,0x07,0x85,0x89,0x29,0x66,0xff,0x15,0x90,0x09,0x24,0xe5,0x79,
++		0xd4,0xfb,0xa0,0x2b,0xda,0x87,0x55,0x5f,0x07,0x3d,0xae,0x00,0x51,0x3e,0x70,0x80,
++		0x9a,0xbb,0xc7,0x11,0xfb,0xa2,0xe7,0x64,0x95,0x77,0xc4,0x2a,0xfd,0xc2,0x4b,0xf7,
++		0x41,0x3e,0x51,0x26,0x8a,0xd6,0xdb,0x61,0x13,0xb7,0xd9,0x19,0x1a,0xf9,0xd0,0x61,
++		0xdb,0xde,0xd5,0xd6,0x30,0x87,0x76,0x50,0xc1,0x24,0xf1,0x1b,0xc4,0xbd,0xc3,0xfd, /*  400 */
++		0xc6,0xa9,0x00,0xf6,0x31,0x26,0xf9,0x21,0xe8,0x38,0xad,0x0c,0x22,0x75,0xa3,0x38,
++		0x9a,0x39,0xbd,0x99,0xa1,0x34,0x50,0x45,0x50,0x10,0x1c,0xd3,0xe9,0x5e,0x6d,0x14,
++		0x96,0xbe,0x7d,0xe6,0x62,0x7d,0xf4,0xfd,0x6c,0x28,0xbb,0xf4,0x0b,0x30,0xef,0xa9,
++		0xb5,0xc3,0xd5,0xc8,0x5a,0xb1,0x4a,0x65,0xc0,0x2d,0x6d,0x47,0x81,0xff,0x13,0xd3,
++		0x28,0x60,0x85,0x54,0xb6,0xd1,0x5e,0xd9,0x12,0x89,0xa6,0xd5,0x5a,0xac,0x0c,0x38, /*  480 */
++		0xe3,0x77,0x06,0xf7,0x35,0x5e,0x9a,0x4f,0xda,0x61,0x5b,0x87,0x59,0x26,0xbf,0xe5,
++		0xa5,0x9d,0x9e,0xf2,0x73,0xbf,0x94,0xa0,0x7c,0xfa,0x57,0x31,0x78,0xf0,0xe0,0x04,
++		0xb6,0xe1,0xef,0x0a,0x83,0x49,0xe9,0xbc,0xc0,0x19,0x81,0xf2,0x46,0x0f,0x0a,0x27,
++		0x43,0xc2,0x8d,0x1e,0x13,0x8f,0xfb,0x76,0x5e,0x7e,0x33,0x97,0xb7,0x91,0x33,0x35,
++		0xd4,0x02,0xfe,0x91,0x80,0x6a,0xa8,0xfc,0x81,0x92,0x53,0xaf,0x32,0x69,0x2f,0xa6, /*  560 */
++		0x51,0xe8,0x67,0xf5,0x90,0x7e,0xf4,0x6f,0x00,0x62,0x5a,0x03,0x0e,0xc9,0x04,0xed,
++		0xab,0x21,0x42,0x6d,0x59,0x11,0x9d,0x2c,0xaa,0x43,0xbd,0x93,0x5d,0xec,0x0a,0x55,
++		0x0c,0x61,0xee,0x4b,0x27,0x9c,0x1c,0xa3,0xa7,0x9c,0x79,0xa6,0x6e,0x3f,0x2d,0x2f,
++		0xad,0xb0,0x0f,0x59,0xa3,0xa4,0x38,0xaa,0x44,0x57,0x01,0x06,0x07,0x30,0x17,0xfa,
++		0x1c,0x87,0x57,0x50,0x01,0x09,0x72,0x0d,0x12,0x5b,0xba,0x23,0x1a,0x0c,0x36,0x35, /*  640 */
++		0x0c,0x78,0x08,0x6d,0xfd,0xc8,0xd6,0x13,0xae,0xca,0x88,0xc4,0xcc,0xae,0xb4,0xa4,
++		0x4d,0x13,0xad,0xb3,0xc7,0x17,0xd6,0x5c,0x82,0xa3,0x51,0xb9,0xb6,0xea,0xbf,0x6a,
++		0x10,0xf4,0xb4,0xe9,0x62,0x3e,0x3a,0x95,0xb4,0xd4,0x0a,0x12,0xa8,0x18,0xac,0x6b,
++		0x38,0x22,0xdb,0x82,0xfb,0x05,0xdc,0x42,0x02,0x64,0x8b,0x44,0x54,0x68,0x9a,0xeb,
++		0x69,0xea,0x32,0x5f,0x03,0xe3,0x5d,0xef,0xa5,0x47,0x08,0x48,0x14,0x20,0xc6,0xd6, /*  720 */
++		0x97,0xbb,0x91,0x2f,0xca,0x0d,0x3f,0x19,0x2e,0xf2,0x97,0xdf,0xe7,0x7f,0xf3,0x6b,
++		0x21,0x03,0xf1,0xad,0x1a,0xee,0xce,0xd1,0xc8,0x14,0xc2,0xcd,0x7e,0xf1,0x6b,0xce,
++		0x47,0x6a,0xd0,0x4f,0x94,0x1a,0xfc,0x79,0xe3,0x29,0x54,0x74,0xa4,0x10,0x62,0x51,
++		0x8c,0x00,0x37,0x86,0x09,0x34,0xf0,0xe5,0xe6,0x52,0xf7,0x27,0x49,0xa6,0x98,0x63,
++		0x2a,0x09,0x91,0xf6,0x13,0xf5,0xcb,0x96,0xca,0x11,0x78,0xf9,0x74,0xf2,0xc4,0xaa, /*  800 */
++		0x0c,0xe6,0x3d,0xc2,0x4e,0x36,0x4c,0x92,0xa6,0x43,0xb9,0x0a,0x5f,0x85,0xa6,0x2f,
++		0xd4,0xd8,0xd2,0xb1,0x93,0xd2,0x9b,0x18,0xbe,0xde,0x26,0x53,0xfc,0x5d,0x3f,0x24,
++		0xf5,0xb2,0xc0,0x18,0xdb,0xbc,0xb6,0xef,0x00,0xf3,0x05,0xbf,0x93,0x66,0x6b,0xd4,
++		0x7f,0xea,0x91,0x93,0xbc,0x23,0x3d,0xb3,0x91,0x21,0x44,0x2e,0x93,0x8d,0xa5,0xdd,
++		0x07,0xee,0x6e,0x87,0x9c,0x5b,0x9d,0xff,0x41,0xec,0xee,0x5e,0x05,0x89,0xae,0x61, /*  880 */
++		0x75,0xff,0x5e,0xc6,0xf6,0xd2,0x62,0x9f,0x56,0xb1,0x8b,0x4d,0xe6,0x6f,0xcb,0x13,
++		0xdf,0x04,0x00,0xa7,0x97,0xc9,0x22,0x70,0xf6,0x9b,0xde,0xbd,0xdc,0xb8,0x8c,0x42,
++		0x48,0x91,0x9b,0x56,0xcd,0xa7,0x0b,0x8a,0xc4,0xf9,0x42,0x9c,0x29,0x2d,0xa9,0x4d,
++		0x64,0x78,0x28,0x07,0x64,0xfe,0x23,0x86,0xfc,0x38,0xcb,0x09,0x31,0x45,0x88,0x39,
++		0xef,0x4e,0x7d,0xe8,0xf0,0x68,0x9d,0x99,0x80,0x59,0x88,0xc7,0xf9,0x61,0x11,0x85, /*  960 */
++		0x2c,0x89,0x29,0xe5,0xa5,0x40,0xd3,0xb7,0x8d,0x71,0x2d,0xec,0xc3,0x96,0xfe,0xf3,
++		0xec,0x34,0x40,0x21,0x84,0xe4,0xfd,0x29,0xf3,0x63,0xea,0x80,0xf6,0xfc,0x50,0xba,
++		0x9a,0x11,0x35,0x1a,0xce,0xea,0x8f,0xe6,0x8d,0x54,0x1e,0x1a,0xa5,0x84,0x8d,0x9f,
++		0x6e,0x61,0xdf,0xb6,0x2b,0x2f,0x23,0xbc,0x50,0x81,0xe8,0x2f,0x76,0x22,0x6e,0x03,
++		0x28,0x49,0x82,0xec,0x48,0x48,0x12,0x09,0xb1,0xa7,0xd4,0xc8,0x79,0x7e,0x44,0xbf, /* 1040 */
++		0xa8,0x70,0xb2,0x20,0x04,0xdb,0x74,0xbd,0x7d,0x47,0x8d,0x5b,0x36,0x14,0xd2,0xb1,
++		0xda,0x75,0x02,0xb3,0x98,0xeb,0x9d,0xa8,0x0d,0x06,0x46,0x1e,0x90,0xe0,0x30,0x60,
++		0x44,0x6a,0xb4,0xa8,0x23,0x84,0x32,0xbf,0xaf,0x75,0x2f,0x39,0x17,0x91,0x21,0x4f,
++		0x1e,0x6b,0x63,0x59,0x0d,0x53,0x60,0x60,0xd1,0xc2,0x45,0x30,0x7b,0xc5,0xc1,0xba,
++		0xc4,0xaa,0xa0,0x99,0xd3,0x6b,0xb6,0xdc,0xbc,0x97,0x3c,0xf2,0xe6,0x9f,0x27,0x34, /* 1120 */
++		0xd0,0xf2,0x9a,0xee,0xc4,0x56,0x7b,0x99,0xa1,0x6b,0xc1,0x7c,0x6c,0xdd,0xac,0xef,
++		0xe4,0x99,0x27,0xfb,0x14,0xe7,0xd9,0x8d,0xd4,0x26,0x35,0x19,0x46,0x9c,0xca,0x3d,
++		0xb4,0x67,0x9a,0x68,0xce,0xed,0xa9,0x55,0x59,0x22,0x10,0xfc,0x49,0xaa,0x5f,0xbe,
++		0x93,0x4c,0xc7,0x3d,0x84,0xe4,0xba,0x54,0x78,0x00,0x2d,0x68,0x90,0x98,0x90,0x68,
++		0xef,0x8f,0xc9,0x8c,0x25,0x32,0xb8,0x3b,0xf3,0xcb,0x9e,0xf0,0x28,0x93,0xc2,0x15, /* 1200 */
++		0x24,0x26,0xb9,0xd1,0xa9,0x47,0x34,0xdf,0xb4,0xf9,0x11,0x35,0x14,0x3c,0x9e,0xed,
++		0x18,0xfd,0x51,0xae,0x87,0x5d,0x07,0xa2,0x37,0x75,0x60,0x6a,0x73,0x4f,0xba,0x98,
++		0xc0,0x63,0xb4,0xa1,0x62,0x2e,0x7f,0xf2,0x1a,0xa7,0xe6,0x52,0xa3,0xd6,0xc1,0x9f,
++		0xe0,0xdc,0x67,0x61,0xb7,0xd3,0x53,0x02,0xbf,0x21,0x4d,0x30,0x79,0xf7,0x60,0x51,
++		0x08,0x2a,0x87,0x59,0x29,0x92,0x0d,0xc3,0xb3,0xcb,0x43,0x21,0x1a,0x23,0xa4,0x3a, /* 1280 */
++		0x50,0x33,0x2f,0xaf,0x1a,0xc2,0x19,0x1e,0x71,0x71,0x25,0xf6,0x3e,0x25,0x86,0xc4,
++		0xd8,0x6d,0xca,0x6b,0xcd,0x3d,0x03,0x8f,0x9d,0x3a,0x7b,0x66,0xcb,0xc7,0xdf,0x34),/* 1312 */
++	  chunk_from_chars(
++		0xd3,0x82,0x2f,0xfc,0x0e,0xee,0x8d,0x42,0x48,0x6e,0xa4,0xa8,0x3a,0x0b,0x3a,0x69,
++		0xcc,0x18,0x96,0x82,0xc1,0xee,0xdc,0x63,0x4a,0xdd,0x30,0xb0,0xd4,0x2a,0xa2,0xab,
++		0x45,0x10,0x88,0xe0,0x0f,0xd2,0x15,0x92,0xeb,0xae,0xe3,0x2f,0xca,0x19,0xa8,0x77,
++		0x81,0x59,0x30,0xe3,0x03,0x3f,0x18,0x30,0x75,0xde,0xc5,0x16,0xac,0x3a,0x07,0x41,
++		0xf5,0x13,0xd7,0xb4,0x7b,0x1a,0x95,0x1a,0x86,0x7a,0x17,0xb0,0xfa,0xb5,0x46,0x22, /*   80 */
++		0x53,0x7a,0xd8,0xd0,0x6c,0x37,0xf0,0x6f,0x3d,0xd9,0x50,0xf8,0xb8,0x8b,0x7d,0xeb,
++		0x2c,0x41,0x8b,0xd3,0xc2,0xff,0x5e,0x7d,0xd3,0x16,0x90,0x06,0xc7,0x25,0xf4,0x54,
++		0x86,0x68,0xc5,0x62,0xef,0xbf,0x2d,0xf5,0x2d,0xf7,0x3b,0x28,0x42,0x74,0x2d,0xe4,
++		0xe3,0x35,0xc0,0x3e,0x75,0x01,0x8a,0x05,0x4a,0x45,0xf3,0x75,0xdd,0x5b,0x4b,0x0c,
++		0x33,0x8b,0x2a,0x8f,0xc5,0x75,0x64,0x92,0x33,0x6b,0xb2,0xd0,0x81,0xa7,0x82,0xa9, /*  160 */
++		0xce,0x24,0xb2,0x92,0xbb,0x31,0x7b,0x05,0xba,0x8a,0xa5,0xf5,0xaa,0x29,0x6c,0x9f,
++		0x9d,0xd9,0x78,0xbf,0xc0,0x42,0x0f,0xb4,0x46,0x52,0x1a,0x76,0x36,0x28,0xeb,0xb7,
++		0xa4,0xf0,0x60,0xfd,0x30,0x57,0x4f,0x4d,0x4a,0x40,0xf9,0x1f,0x5b,0xbd,0xf4,0xdf,
++		0xd5,0xf1,0x8b,0x5e,0x64,0xf5,0xb4,0xe6,0x82,0x72,0x8a,0xe8,0x9b,0xa6,0x04,0x48,
++		0xf8,0xfa,0x5f,0x03,0x3b,0x67,0x26,0xf0,0xa7,0xf1,0xa5,0xcc,0x1c,0x80,0x74,0x36, /*  240 */
++		0xa0,0x42,0x81,0x44,0x8a,0x44,0xb4,0xeb,0xfe,0xa4,0x24,0x9d,0xee,0x96,0x04,0xc9,
++		0x61,0x12,0x1b,0xd2,0x28,0x3a,0x75,0x3f,0xf4,0x92,0x8b,0xa8,0x94,0x36,0x2e,0xd4,
++		0x06,0x36,0xec,0xb7,0xed,0xc0,0x28,0xaa,0x93,0xfe,0xfe,0x66,0x47,0x6b,0xb0,0xda,
++		0x3f,0x93,0xa5,0x63,0x80,0xd1,0x8b,0xe4,0xce,0xc7,0x73,0xe8,0x50,0x04,0x5e,0x41,
++		0x52,0x21,0x2f,0x36,0x66,0x9a,0x8f,0xde,0x53,0x99,0xcf,0x26,0x1a,0x82,0x19,0x6a, /*  320 */
++		0x8a,0x20,0x3f,0x77,0x7b,0xf7,0xad,0x3b,0xf1,0xb2,0x69,0x77,0x5e,0xe0,0x9c,0xc8,
++		0xdb,0x70,0x34,0xf7,0x41,0x57,0x44,0x43,0xb8,0x2d,0x4a,0x9e,0xbb,0x69,0x75,0x1b,
++		0xa1,0x0c,0x52,0x67,0xf3,0x80,0x6b,0xb1,0xcd,0x13,0xf5,0x02,0x55,0x1c,0x12,0x6b,
++		0x75,0x42,0xc4,0x05,0x77,0x12,0xcd,0xb3,0x3c,0x4b,0xa8,0xe2,0x65,0x53,0xff,0xac,
++		0xfd,0x7c,0x92,0xb3,0x10,0x14,0x80,0x8f,0x78,0xc8,0xe9,0xca,0x2b,0x61,0xc0,0x7e, /*  400 */
++		0x45,0xe1,0x98,0xed,0xbc,0xc1,0x0c,0xfe,0x21,0xed,0x79,0x92,0x0f,0xed,0x42,0x05,
++		0xd6,0xec,0xbb,0x10,0xd1,0xfd,0x37,0xd2,0x04,0xab,0x1b,0x60,0x54,0x46,0xc2,0x7a,
++		0x44,0xc6,0xeb,0xad,0xe1,0xf0,0x99,0x2f,0x10,0xe2,0xf3,0x80,0xc4,0xd2,0x13,0xa3,
++		0x97,0x29,0xef,0x34,0x97,0xc6,0x57,0x2c,0x29,0x12,0x7d,0x1a,0x2f,0x8e,0x27,0x4f,
++		0x23,0xd1,0x10,0xcb,0xac,0x00,0xe2,0x89,0xbf,0x95,0x40,0x93,0x75,0x77,0xde,0x38, /*  480 */
++		0xe9,0x5b,0xac,0xa5,0x51,0x0b,0x34,0x11,0x41,0x93,0x8f,0xa7,0x5d,0x8d,0x47,0xf6,
++		0x33,0x3a,0x3b,0x2a,0xa9,0xd8,0x58,0x1d,0x59,0x7f,0x5b,0x23,0x1f,0xee,0x59,0xa7,
++		0x89,0xd2,0x99,0x4d,0x39,0x8b,0x51,0x4c,0x39,0x57,0x46,0x67,0x72,0xfd,0xa6,0xbb,
++		0xfd,0xc8,0x59,0xe3,0x48,0x32,0x88,0xe1,0x02,0x45,0xc2,0xca,0xa4,0xbb,0x8a,0x13,
++		0x33,0x98,0xf0,0x47,0xf2,0x40,0xde,0x9f,0x2c,0x15,0xe3,0x67,0x8f,0xd9,0x59,0x35, /*  560 */
++		0xa0,0x2c,0x20,0x96,0x0d,0x2f,0x5f,0x84,0x69,0x40,0x99,0xc5,0x71,0x39,0x70,0x8c,
++		0xb4,0xd7,0xa7,0x85,0xe0,0xd7,0xe9,0x52,0x9b,0x7e,0x59,0xe0,0x36,0x59,0x36,0x59,
++		0x0f,0x17,0x11,0x66,0x58,0xde,0x80,0x8c,0x3f,0x75,0x50,0x0f,0x5d,0x58,0xf3,0x97,
++		0xed,0xf1,0xf0,0x52,0x3b,0x07,0xb8,0x40,0x6d,0xf9,0x68,0xcc,0x0b,0x09,0xb8,0x84,
++		0x92,0xfe,0x00,0xa7,0x35,0xb2,0xca,0xcd,0x6f,0xcf,0x46,0x08,0x86,0x20,0x05,0x2b, /*  640 */
++		0xc7,0x12,0x09,0xb0,0xab,0x96,0xc8,0xcf,0x82,0x7c,0xed,0xe5,0xa9,0x39,0x3a,0x26,
++		0x3f,0x6b,0x01,0x51,0x5e,0xb9,0x4b,0x4c,0x7b,0x4f,0x6a,0x52,0xc8,0xa2,0x32,0x14,
++		0x8b,0x15,0x02,0x71,0xd4,0x6a,0x17,0xe6,0x6d,0x9d,0x71,0xa0,0x9a,0x1a,0x82,0x2d,
++		0x09,0xe9,0xee,0x31,0xfb,0x09,0x36,0x6a,0xc7,0x6d,0x2c,0x4d,0x20,0x44,0xc6,0x1e,
++		0x28,0x5e,0xe7,0x48,0x22,0x78,0x78,0x25,0x8f,0xa0,0xf5,0x3f,0x19,0x96,0x62,0x33, /*  720 */
++		0x1a,0x9e,0xee,0xcc,0xbc,0x47,0xea,0xf3,0xb4,0x1d,0xd4,0xd7,0x47,0xb1,0xe4,0x8e,
++		0x6a,0x93,0x05,0x85,0x9b,0x49,0x9e,0x19,0xb6,0xf2,0x83,0x95,0x1f,0x60,0xab,0x89,
++		0xf0,0xbf,0x85,0xea,0x5a,0xc0,0x7a,0x27,0x3e,0x1a,0xcf,0xc9,0x62,0x96,0xe7,0x9a,
++		0x15,0x6d,0xc8,0x32,0x11,0xa2,0xc2,0xb1,0x24,0x32,0x36,0xc5,0xa8,0x78,0x9f,0xbd,
++		0x2a,0xe6,0x7f,0xc3,0xfa,0x74,0x09,0xab,0xf9,0x04,0xfe,0xdf,0x09,0xfa,0x9e,0x2a, /*  800 */
++		0x3e,0x1f,0x09,0xc5,0xec,0xe9,0xd2,0xcc,0xf9,0x14,0xd7,0x7f,0x95,0x88,0x0c,0xd9,
++		0xf7,0x28,0x2d,0xa1,0xe9,0x4b,0x2e,0x72,0x75,0x2c,0x9b,0xf2,0x38,0x4f,0x6b,0xa1,
++		0x15,0x3c,0x8a,0xe3,0x23,0x8a,0x56,0x69,0xae,0x51,0x54,0xc4,0xbb,0x2b,0xf9,0x5a,
++		0x09,0xc1,0xd5,0xf8,0x9e,0xaf,0x76,0x98,0x9c,0xfd,0x67,0x57,0xa5,0x30,0x84,0xba,
++		0xc1,0xbd,0xf6,0x70,0x41,0x0f,0x62,0x31,0x0c,0x27,0x5c,0x9e,0x45,0x45,0xe2,0x9d, /*  880 */
++		0x8b,0x74,0x2d,0xbd,0x67,0x8d,0xb6,0x13,0xe1,0x05,0xa1,0xd3,0xca,0xed,0xe3,0xa2,
++		0xc9,0xba,0xcd,0x95,0x2f,0xe7,0x76,0x75,0xcd,0x7e,0x11,0xbe,0xac,0x67,0xe4,0x96,
++		0xb5,0xaa,0xfd,0x39,0x44,0xae,0x2a,0x06,0x5a,0xe2,0x0e,0x8d,0xf2,0x7a,0xb9,0x1a,
++		0xfd,0x50,0x35,0x47,0x3a,0xc0,0xd9,0x83,0x89,0x32,0x80,0xfc,0x1e,0x93,0x7a,0xa3,
++		0x74,0x23,0xed,0x54,0x03,0xc6,0xd1,0x15,0xff,0x3f,0x1c,0x6d,0xaa,0x93,0x92,0x30, /*  960 */
++		0x0c,0x27,0xfe,0xff,0xfc,0x34,0x21,0x87,0x19,0xda,0xe6,0x29,0x4b,0x78,0xc6,0x29,
++		0x64,0x30,0x54,0x6b,0x62,0xc7,0xb8,0x03,0x83,0xc0,0x73,0x04,0x6f,0xcd,0xef,0x11,
++		0xde,0xf7,0x1a,0xc6,0xc0,0x53,0x08,0xf2,0x4c,0xa3,0x6b,0x14,0xce,0xc4,0xce,0x92,
++		0xf6,0x8a,0x0d,0x35,0x5b,0x88,0xad,0x2d,0x7d,0x7e,0x35,0xaa,0xf3,0x9d,0x23,0xa8,
++		0x66,0x31,0x42,0x28,0x84,0x53,0x20,0x97,0x97,0xbe,0x96,0xc9,0x1a,0x06,0x81,0x1e, /* 1040 */
++		0x12,0xc4,0xb5,0x7d,0xaf,0xd9,0x1d,0x00,0xa8,0xdf,0xfe,0x74,0xba,0x61,0xa5,0xaa,
++		0xb0,0x20,0x69,0x92,0x37,0xdd,0xf7,0xce,0xff,0xac,0xc7,0x5d,0x8f,0xff,0x0b,0x8e,
++		0xe1,0x5b,0x75,0xfa,0xeb,0xa1,0xc9,0xb9,0x78,0xfa,0xf5,0x26,0x84,0xb6,0x41,0xe1,
++		0x91,0xe8,0x55,0x7d,0x6c,0x94,0x34,0x59,0x5a,0x73,0xfd,0xcf,0xdd,0xc1,0xc0,0xae,
++		0x67,0x4e,0x38,0x97,0x46,0x28,0x91,0x25,0xa6,0x96,0x7c,0xeb,0xce,0xf6,0xd4,0x79, /* 1120 */
++		0x55,0xac,0xcc,0x19,0x77,0x92,0x07,0xac,0x06,0x55,0xb7,0x19,0xa7,0xec,0xfe,0xc7,
++		0x91,0x25,0x6a,0xd0,0xa4,0xb4,0xc9,0x0f,0xe7,0xf8,0xdd,0x13,0x60,0xcb,0x93,0x6c,
++		0x69,0xef,0x87,0xf8,0x31,0xa9,0x2b,0xdf,0xd9,0xdf,0x2b,0xd2,0x71,0xfb,0x95,0xcd,
++		0x41,0xc0,0x64,0x30,0x57,0xbe,0x79,0x81,0x1f,0x43,0x51,0xff,0x69,0xcf,0x61,0x58,
++		0xa9,0x4b,0x89,0x17,0xf0,0x93,0xc8,0x4f,0x49,0x5b,0x3e,0x62,0x98,0x60,0x28,0xee, /* 1200 */
++		0x4a,0xf8,0xe2,0x25,0x53,0xc0,0x71,0xa0,0x02,0x7c,0x84,0x29,0xb3,0x78,0x0c,0xd5,
++		0x54,0xdd,0x0c,0xf4,0x96,0x37,0xd4,0xea,0x6f,0x06,0xce,0xef,0x4c,0x19,0x74,0xe1,
++		0xcc,0xab,0x02,0xdd,0x28,0x5e,0xd7,0x5c,0x4a,0xe9,0xcd,0xe3,0x5e,0xca,0x4f,0xf2,
++		0x79,0xe3,0x30,0x0c,0xbb,0x70,0x39,0x02,0x33,0x33,0x04,0xd1,0x7f,0xd1,0x02,0x6f,
++		0x7f,0x87,0x8b,0x86,0x77,0x75,0x29,0xdd,0x72,0x06,0xc8,0xa0,0x06,0x91,0x7b,0x53, /* 1280 */
++		0x39,0xd5,0xef,0x18,0x65,0xd9,0x3e,0x1b,0xbe,0x80,0x4a,0x3c,0x2b,0x65,0xaa,0xcf,
++		0xb2,0x8f,0x82,0x8d,0xbb,0x65,0xeb,0xc8,0x8d,0x6e,0x3e,0x20,0x2b,0xa7,0x9f,0x35,
++		0x63,0xb1,0x8c,0xb4,0xa3,0x93,0xb2,0x18,0xea,0xc1,0xc3,0xd5,0x53,0x4e,0x34,0x25,
++		0x6d,0xa4,0xd0,0x4b,0x03,0x6a,0xc9,0x3b,0x0c,0x39,0x21,0xd1,0x99,0x5b,0x72,0x12,
++		0xd4,0xb0,0x0e,0x4c,0x89,0x7d,0x03,0xd5,0xdf,0xa8,0xd0,0x6e,0x2f,0x9a,0x02,0x64, /* 1360 */
++		0xd1,0xb7,0xb3,0x5d,0x4e,0xb1,0x3f,0xaf,0x88,0x32,0x28,0x24,0xb4,0xa6,0x88,0x63,
++		0x08,0x95,0xd3,0x36,0xa3,0x8f,0xb7,0xd8,0xfe,0xf9,0xf8,0xc9,0xae,0xaf,0x0d,0xfd,
++		0x0d,0x8e,0x00,0x46,0xe4,0xb9,0x4a,0x1a,0x88,0xe4,0x30,0xc7,0x66,0x17,0xa6,0x6a,
++		0x28,0x4c,0x48,0x05,0x34,0x79,0xff,0x48,0x38,0x64,0x32,0x6b,0x9b,0xa5,0x27,0x1e,
++		0x6d,0x7b,0xe8,0x85,0x0d,0xc6,0x5e,0x80,0xd9,0x2a,0x49,0x5c,0x7c,0xee,0x79,0x94, /* 1440 */
++		0x8d,0xe5,0xe0,0xaf,0xb5,0x59,0x78,0x09,0xfe,0x5e,0xd4,0x43,0xd4,0x0f,0xad,0x41,
++		0xe1,0x0b,0x13,0x6a,0x15,0xa5,0x4a,0xbb,0x56,0x6e,0x7f,0xff,0x5b,0x85,0x19,0x5e,
++		0xcf,0x83,0x32,0x8f,0xad,0xaa,0xf1,0x9f,0x9c,0x16,0x7f,0xc8,0x8c,0x52,0xbc,0xd5,
++		0x35,0x57,0xce,0x50,0xf5,0xf3,0xe3,0xe5,0x2a,0x70,0x17,0x11,0x33,0xf9,0xb8,0x14,
++		0xaf,0x37,0xb4,0x8f,0xc6,0xfe,0xfb,0xc6,0x6b,0x82,0x31,0x72,0x76,0x58,0xc4,0xa5, /* 1520 */
++		0xec,0xb8,0x2d,0x4a,0x31,0x21,0xa9,0xbc,0x16,0xe8,0x6e,0x0b,0x79,0x95,0x8e,0x65,
++		0x9f,0xaa,0xc4,0x5b,0xe6,0x50,0x28,0x71,0x0c,0xc8,0x49,0x35,0x16,0xae,0xee,0x60,
++		0xdb,0x29,0x74,0x7e,0x0d,0xe9,0x83,0xa1,0x6d,0x95,0xbd,0xf3,0x33,0x1b,0x19,0x76,
++		0x83,0xfd,0x55,0x0d,0xdb,0xfd,0x55,0xb4,0x01,0xdd,0x3b,0xe6,0x89,0x18,0x61,0x3c,
++		0x59,0xed,0xa8,0x18,0xff,0xfa,0x15,0xe4,0xde,0x8b,0x5b,0x72,0x79,0x91,0x9a,0xc7, /* 1600 */
++		0xb3,0x92,0x19,0x58,0xeb,0x50,0x72,0xcb,0x2a,0x55,0xdc,0x56,0x79,0x9d,0x05,0x2c,
++		0xbb,0x95,0x34,0x11,0xba,0xed,0xa9,0x82,0x36,0x9a,0x7a,0x41,0x46,0x3e,0x45,0x0c,
++		0xc0,0xe7,0x41,0x09,0xbd,0x7b,0x41,0xc8,0xeb,0x38,0x5e,0x27,0xa2,0xf3,0x8d,0xc4,
++		0x94,0x36,0xf3,0x06,0x8a,0x4b,0x9f,0x14,0x0f,0xbb,0x3e,0x30,0x63,0x5c,0x13,0x78,
++		0xae,0xa3,0xda,0x94,0xd0,0xf2,0x80,0xb8,0x8a,0x07,0xd4,0x0e,0x66,0x52,0xf6,0xbb, /* 1680 */
++		0x17,0x6d,0x8e,0x4d,0xea,0xc4,0x25,0xce,0x7f,0xe5,0xb1,0xdd,0xc1,0x86,0xae,0x1e,
++		0x1a,0xd4,0xe1,0xb1,0x14,0xbb,0xcd,0x40,0x25,0x23,0x4d,0x2e,0x25,0x3b,0x11,0x5e,
++		0x0b,0x3a,0xd8,0x38,0xb1,0x20,0x31,0x6e,0x80,0x6c,0x10,0xd6,0x8e,0xd0,0xcc,0x14,
++		0x4c,0x70,0xa6,0x5e,0x8a,0x4f,0xc5,0x29,0x7e,0x76,0xba,0xf5,0x7c,0xd3,0xc8,0x6c,
++		0x4f,0x36,0xf0,0x88,0x78,0x1f,0x04,0xb2,0x47,0x61,0xe6,0x94,0xd3,0xea,0xe7,0x26, /* 1760 */
++		0x63,0xc3,0xe4,0x3d,0x9c,0x05,0xfd,0xdb,0xab,0x95,0xd0,0xae,0x23,0x37,0xf2,0xb3,
++		0x7f,0xb1,0xd5,0x5a,0x07,0x40,0x10,0xd8,0x24,0x2f,0x5c,0x9c,0x05,0xf7,0x18,0xa8,
++		0x7e,0x36,0x4b,0xb3,0x64,0xe5,0x67,0x47,0xb9,0x5e,0xd9,0xde,0x65,0xb8,0x6c,0xd2,
++		0xf8,0x71,0xde,0x45,0x6c,0x83,0xe6,0xce,0x18,0x3e,0x26,0x2d,0x6a,0xf5,0xcf,0x26,
++		0x11,0x95,0xd6,0xdc,0xdf,0x43,0xc6,0x2d,0x77,0x85,0x63,0xb2,0x48,0xdc,0x01,0xc0, /* 1840 */
++		0x8a,0xcb,0x39,0xc4,0x14,0x19,0x08,0xa1,0x22,0xba,0xf0,0xfa,0x76,0xa4,0xc9,0x1b,
++		0xcc,0xd8,0x37,0xf8,0xa2,0x88,0xc6,0x7e,0xf4,0xe2,0xf4,0xaa,0xe4,0x38,0x1b,0xdf,
++		0xcd,0xc0,0xb9,0x89,0xfd,0x19,0x30,0x70,0x9f,0x6a,0xbc,0x7b,0x37,0x9a,0xff,0x7c,
++		0xe8,0x2a,0x64,0xcd,0x4f,0x3a,0xe7,0x37,0xac,0xfe,0x98,0x45,0x19,0xdd,0xd4,0xb8,
++		0xfe,0x8a,0x76,0xde,0x52,0xd1,0xa7,0xf2,0x76,0x72,0x1b,0xce,0x06,0x00,0x84,0x11, /* 1920 */
++		0x72,0xe7,0xfa,0x02,0xa3,0x23,0xfb,0xf4,0x9c,0xe7,0x1d,0xd0,0xbc,0x7f,0x47,0xad,
++		0xe0,0xf8,0x72,0xb9,0x6c,0x65,0xc9,0xf3,0x74,0xb5,0xdc,0x7b,0x80,0x99,0x5a,0x8e,
++		0x35,0xc2,0x7d,0xdf,0x58,0xac,0xb9,0xff,0x10,0x7e,0xc8,0xd6,0x42,0x43,0x6d,0xb1,
++		0x9d,0x36,0x67,0x61,0xda,0x7e,0x68,0x46,0x94,0xd6,0x73,0x1e,0xea,0x9f,0x43,0xf2,
++		0x46,0x62,0x54,0xc3,0x0e,0xb7,0xef,0x1f,0x1b,0xa1,0x59,0xc6,0x22,0x88,0x69,0xdc, /* 2000 */
++		0x49,0x09,0xf6,0x18,0x25,0xb9,0x25,0xe7,0x3b,0x1c,0x1d,0xf0,0x40,0xcf,0xb9,0x4d,
++		0x7b,0xe7,0x81,0x79,0xb8,0xb8,0x89,0x12,0xd8,0x59,0x17,0xd4,0x4d,0x66,0x8e,0xbe,
++		0x78,0xff,0xbe,0x48,0x86,0x82,0x73,0x40,0xcb,0xff,0xbe,0x68,0x2c,0xfb,0x45,0xe9,
++		0x9f,0x39,0x62,0x4f,0xfc,0x59,0xe2,0x7f,0x62,0xd3,0xdb,0xed,0x9d,0xbb,0xd4,0xff,
++		0x7c,0xd6,0x46,0x93,0x2f,0xc1,0xdb,0xde,0x3c,0x25,0xac,0xfd,0xc5,0x59,0x61,0xfc, /* 2080 */
++		0xbd,0x43,0xda,0x4d,0xd2,0xc6,0x39,0x87,0x17,0x2a,0x87,0xc8,0x4f,0x89,0x58,0x8a,
++		0x5a,0x1d,0xaa,0x23,0x3d,0x0c,0x31,0xe3,0xe5,0xab,0xd9,0x6c,0xf4,0x5d,0x33,0xa3,
++		0x17,0xc4,0xd6,0xc4,0x3d,0xb4,0x93,0xe1,0x8d,0xd2,0x12,0xb3,0xd2,0x70,0x9a,0xef,
++		0x24,0x6e,0x17,0x74,0x1e,0x3a,0x84,0xe6,0xc8,0xbc,0x3c,0x9c,0x51,0x7a,0xd0,0xad,
++		0xc6,0x71,0x91,0x41,0xd1,0xc0,0xfa,0xf3,0x98,0xb4,0xce,0xb8,0x5b,0x03,0x25,0x50, /* 2160 */
++		0x84,0xee,0x88,0x51,0xf7,0xe3,0x06,0x5d,0x42,0xc8,0x90,0xbe,0xc9,0x9f,0x19,0xf1,
++		0x22,0x75,0x70,0x03,0xc8,0x39,0xc5,0x30,0x72,0x9a,0x36,0x1e,0x55,0x65,0x52,0xd0,
++		0x10,0xf1,0xc9,0x51,0x0d,0x85,0x4d,0x4a,0x6e,0xb9,0x7a,0x96,0x27,0x97,0x2e,0x84,
++		0x8b,0xc6,0x8c,0xda,0x07,0xdc,0x87,0x20,0xb3,0xe4,0xef,0x89,0x66,0x61,0xdd,0x27,
++		0x7d,0xed,0x16,0x76,0x58,0xd1,0x36,0x7d,0xc0,0x14,0x94,0xb7,0x90,0x4e,0xac,0x8d, /* 2240 */
++		0x59,0xfa,0x77,0xa7,0x04,0xa4,0x43,0x4e,0xf0,0x3f,0xac,0x2e,0xfd,0x06,0xbc,0x06,
++		0x12,0xd9,0xd8,0x62,0xc3,0xff,0x8c,0xf5,0xfd,0xa5,0xdc,0xce,0xfe,0x38,0xf7,0x6c,
++		0x89,0xbf,0x47,0x62,0xb4,0x08,0xda,0xed,0x0e,0x8f,0xee,0xca,0x45,0xa5,0x74,0xb3,
++		0x7f,0x40,0xad,0xa0,0x24,0x9e,0x50,0x30,0x24,0x41,0xaa,0x05,0xf9,0xb1,0x14,0xdc,
++		0xdc,0x5a,0xb0,0x32,0xf5,0x56,0xa1,0x9f,0xe3,0x51,0x26,0xbf,0x42,0x5f,0x0a,0x38, /* 2320 */
++		0x55,0x41,0x62,0xed,0xc1,0xb2,0x2e,0x66,0xe4,0x58,0xe4,0x1f,0xfe,0x89,0x9f,0xc7,
++		0x00,0x01,0x30,0x33,0x39,0x48,0x4c,0x69,0x6a,0x7b,0x98,0x9a,0xa3,0xdf,0xeb,0xf8,
++		0x1c,0x1e,0x2e,0x56,0x5b,0x6d,0x83,0x9e,0xc1,0xc4,0xca,0xd4,0xe4,0xe9,0xea,0xf9,
++		0x2a,0x34,0x3a,0x66,0x67,0x6f,0x74,0x81,0x8a,0xb5,0xbb,0xf6,0x0d,0x15,0x22,0x36,
++		0x5b,0x5c,0x60,0x61,0x71,0x77,0x82,0x87,0x98,0x9e,0xa9,0xac,0xad,0xde,0xe6,0xea, /* 2400 */
++		0xf8,0xf9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		0x10,0x20,0x2c,0x42),                                                            /* 2420 */
++	  chunk_from_chars(
++		0x72,0xaf,0x19,0x51,0x61,0x78,0xf2,0xd3,0x70,0x78,0x90,0xd4,0x87,0x36,0x8e,0x79,
++		0xb1,0xc8,0xf0,0x20,0x95,0xd5,0xf8,0x76,0x77,0xdf,0x94,0x6c,0x59,0x21,0x48,0xe4,
++		0x58,0x31,0x27,0xd6,0xaf,0x5c,0x69,0x55,0x90,0xab,0x91,0xd4,0x88,0xf1,0xbb,0x12,
++		0x44,0xf7,0x12,0x29,0xd8,0xa4,0xd1,0xa7,0x39,0x0a,0x85,0xa9,0xbe,0xe1,0x2b,0xff,
++		0xd0,0xa9,0xa2,0x08,0xa6,0xf4,0x54,0x37,0x2c,0x51,0x75,0xb3,0x94,0x79,0x1d,0x2a, /*   80 */
++		0xea,0x38,0x04,0x89,0xe5,0x02,0x68,0x8f,0x2a,0x4c,0x0c,0xe0,0xf5,0x47,0xa1,0x86,
++		0x6a,0x05,0xab,0x72,0xa4,0xa7,0x38,0x18,0xd8,0xbd,0x1f,0x9e,0x34,0xaa,0x2a,0xa1,
++		0x50,0xc0,0xc8,0xf1,0xde,0x33,0x3c,0x5d,0xa4,0x2f,0x50,0xe2,0x2b,0xc8,0x07,0x57,
++		0x57,0x86,0xe4,0x48,0x6d,0x4a,0x6e,0x2e,0x1a,0x57,0x74,0xc6,0xe2,0xec,0x2b,0xf5,
++		0x6b,0xce,0x80,0x8c,0x98,0x10,0xdd,0xed,0x5f,0x04,0xa9,0x1d,0x4f,0xd5,0xd0,0x0c, /*  160 */
++		0x08,0xe6,0x0e,0x11,0x90,0x48,0xbb,0xe7,0xa3,0x0c,0x54,0xd0,0xcd,0x3e,0xe2,0x87,
++		0x8f,0x6b,0xec,0x5e,0xcb,0x60,0x15,0x1e,0x5d,0x18,0xcc,0x1f,0x41,0x0d,0xc3,0x4b,
++		0x3d,0xc0,0x9c,0xec,0x70,0xd0,0x2d,0x40,0xbb,0x59,0xbb,0x09,0xe2,0x79,0xbb,0x8a,
++		0x03,0xad,0xc6,0x6d,0xba,0xae,0xa9,0xfd,0x0f,0x65,0xce,0x86,0x63,0xee,0x42,0x38,
++		0x83,0x5e,0x3e,0x98,0x4d,0x5e,0xf5,0x4a,0x9f,0x0a,0x1a,0x67,0xfd,0x38,0xc7,0xfc, /*  240 */
++		0x97,0x84,0x20,0xa2,0x18,0x7c,0x7b,0xd6,0x3c,0xb3,0x16,0x8f,0x4b,0xf5,0x55,0x84,
++		0xfc,0xec,0x37,0xad,0x4b,0x6a,0xac,0x34,0x30,0x87,0x86,0xb1,0x6e,0x51,0x4f,0xc1,
++		0x70,0x1e,0xf5,0x95,0xde,0xbe,0xea,0x12,0xd1,0x94,0x24,0x75,0x34,0x13,0x9b,0x47,
++		0x6b,0x68,0xf8,0x6c,0xaa,0xc6,0x3e,0xa2,0x59,0x4b,0xf4,0xc9,0x00,0x36,0x41,0x58,
++		0x99,0x9a,0x80,0x5b,0x80,0x66,0xe4,0xe0,0xa8,0xc5,0x5e,0x3f,0x30,0x5a,0x68,0xce, /*  320 */
++		0xcf,0x29,0x06,0x27,0x90,0xcd,0xc2,0x09,0x41,0xd1,0xa0,0x8e,0x73,0xfc,0xdc,0x67,
++		0x86,0x40,0x52,0x0e,0x99,0x44,0x7c,0xde,0xe5,0x48,0x09,0xb3,0x58,0x36,0x17,0xf3,
++		0xfc,0x91,0x10,0x21,0x1d,0x33,0x9b,0x25,0x70,0x12,0xf4,0x99,0x83,0x93,0xa3,0x27,
++		0x57,0xc7,0xf0,0x25,0xdf,0x58,0xb7,0x0e,0xd1,0x36,0xc7,0xbf,0x6e,0xf8,0x1d,0xd1,
++		0x7a,0x3d,0x7b,0xa6,0xdf,0xcf,0x88,0xd3,0x2c,0x74,0xe9,0x89,0x19,0x11,0xcb,0xd3, /*  400 */
++		0xff,0xb3,0x39,0x11,0x48,0x56,0x67,0xdd,0x4c,0x85,0x3d,0xc6,0xf6,0xbd,0x77,0x31,
++		0x41,0xa4,0x75,0xb0,0x09,0xeb,0x06,0xfd,0xe8,0xd4,0xfb,0xf2,0x09,0xd0,0x4f,0x6a,
++		0x05,0x63,0xd1,0x4a,0xe0,0x6d,0xb1,0x22,0xe6,0x92,0x61,0x59,0x1a,0xf9,0x6b,0xd4,
++		0xa1,0xb3,0x13,0x41,0x34,0x34,0x57,0xc6,0x60,0x45,0xdb,0xba,0xea,0x4f,0x88,0xb3,
++		0x37,0x54,0x1f,0xd6,0x9a,0x24,0xa1,0x67,0x8f,0x5c,0x5a,0x60,0x7d,0xc5,0xb0,0x79, /*  480 */
++		0xa5,0xb9,0xff,0xc0,0x2e,0x35,0x25,0x5f,0x9e,0xbf,0x86,0x12,0xe1,0x82,0x15,0x28,
++		0x0f,0xe2,0x1d,0x5b,0xaa,0xe8,0x6d,0x66,0x6b,0x2b,0x60,0x6e,0x6f,0xdb,0x80,0xb8,
++		0x38,0x3e,0xfe,0xc6,0xfe,0xd6,0x43,0x7e,0xe1,0x3c,0xa0,0x98,0xb6,0xcc,0xd0,0x1b,
++		0x0d,0xb3,0x56,0x57,0xf8,0x35,0x7d,0x4c,0x34,0x2e,0x9f,0x21,0x13,0x93,0x56,0xf0,
++		0xcd,0x1b,0x6f,0x94,0x30,0x13,0x8e,0x44,0xf8,0x0c,0xcd,0x4f,0x9f,0x93,0x67,0xe7, /*  560 */
++		0x58,0x54,0xa3,0x07,0x71,0xba,0xcd,0xbf,0x12,0x7d,0xf3,0x10,0x5b,0x32,0xd6,0xcf,
++		0xff,0x4a,0x27,0x61,0xa8,0x58,0x32,0xb6,0x42,0xe6,0xb4,0x1c,0x78,0x60,0x95,0x40,
++		0xbc,0x93,0xb3,0x50,0xd3,0x22,0x16,0xde,0x36,0x8c,0x81,0x7d,0xb5,0x1a,0xfc,0x5c,
++		0xc9,0x70,0x32,0xca,0x18,0x7e,0xb0,0x09,0x01,0xa4,0x16,0x34,0xbb,0x96,0xde,0xe0,
++		0x42,0xab,0x07,0xec,0xb3,0xc8,0xf8,0x57,0xae,0xf0,0xcc,0xe4,0xe7,0xe5,0x38,0x90, /*  640 */
++		0x2e,0xee,0x46,0x87,0xec,0x75,0xa6,0xf1,0x73,0x7b,0x80,0x70,0x57,0xce,0x9e,0xe1,
++		0xf1,0xf6,0x4e,0xab,0xb5,0x91,0x76,0x1e,0xb2,0x1b,0xbb,0x12,0xb7,0x8e,0x1b,0x6c,
++		0x88,0x21,0xf1,0x2c,0x99,0xbd,0xc6,0xcd,0x40,0xd2,0x49,0x1f,0xb4,0xe2,0xf0,0x11,
++		0xa2,0x7d,0xc1,0xb7,0xa3,0x4f,0x2c,0xfb,0x10,0x02,0xdc,0x5e,0x18,0x24,0x6c,0x28,
++		0xfe,0xe7,0x99,0xab,0x87,0x2d,0xe7,0x67,0x94,0x3d,0xa9,0xb3,0xa0,0xd8,0x31,0x54, /*  720 */
++		0xcd,0xde,0x45,0x3d,0x2a,0x9d,0x81,0x61,0x18,0x90,0xb8,0x2d,0x4d,0xf2,0x6d,0xec,
++		0x37,0x9e,0xbe,0x72,0x06,0x12,0x29,0xf1,0x99,0x02,0xeb,0xc8,0x16,0x66,0x2a,0x39,
++		0x49,0x44,0x46,0x0f,0xd2,0x5a,0x5a,0x8a,0x99,0xc5,0xcd,0xa2,0x85,0x43,0x6f,0x47,
++		0xc7,0x10,0x3c,0x19,0x46,0x96,0xfd,0xb4,0x56,0x62,0x64,0x30,0xbc,0x93,0x76,0x66,
++		0xe0,0x5c,0x8d,0x17,0x7a,0xb7,0x20,0xfc,0x52,0x30,0x27,0x41,0x26,0xb2,0x1e,0x78, /*  800 */
++		0x8e,0x9c,0xd2,0x20,0x0f,0x2d,0x73,0x16,0x55,0x23,0xc3,0x00,0xf9,0x63,0xc6,0xe0,
++		0x3e,0xdf,0x98,0x6b,0xa4,0xb6,0x9f,0x3a,0x07,0xf6,0x9a,0xa5,0x5b,0x49,0x8a,0xfe,
++		0xdf,0x0e,0xe3,0xd2,0x14,0x34,0xc5,0x1a,0x66,0x40,0x65,0x36,0x3b,0x99,0xb5,0xbe,
++		0x60,0x86,0xd8,0xc2,0x3d,0xd9,0x29,0x08,0x35,0x46,0x99,0x93,0x33,0x89,0x65,0xd1,
++		0xba,0x5c,0xe4,0x95,0xae,0x39,0x99,0xa6,0xa8,0xac,0xe9,0x54,0x57,0xa2,0x69,0x78, /*  880 */
++		0x37,0x76,0x34,0x52,0x71,0x6a,0x63,0x09,0x70,0xd6,0x00,0x3e,0x67,0x5a,0x59,0x79,
++		0x49,0x72,0x22,0xac,0x5b,0xd4,0x36,0x5e,0x36,0x27,0x3c,0x87,0x9d,0xb4,0x24,0x48,
++		0x5e,0x69,0xc9,0xc3,0x10,0x96,0x38,0x60,0x62,0x98,0xd2,0xb4,0xa3,0xf0,0x2d,0xb0,
++		0x10,0x94,0x48,0x23,0x12,0x58,0x75,0x9c,0x06,0x0a,0x8e,0x72,0x44,0xe2,0x7a,0x08,
++		0x90,0x72,0x63,0xed,0x34,0x78,0x41,0x69,0x1e,0x2a,0x1d,0x39,0x5a,0x8c,0x83,0xe6, /*  960 */
++		0xd7,0x93,0xdc,0x5b,0x60,0x4c,0xf2,0x78,0x2e,0xfb,0xa1,0x43,0x3a,0x44,0x05,0x7d,
++		0xa3,0xe1,0xb1,0x36,0x83,0xa2,0x21,0x30,0x43,0xf7,0x22,0x61,0x3b,0x8a,0x0a,0xfb,
++		0x59,0xef,0x21,0xa0,0x46,0xda,0x4e,0xe8,0x53,0xad,0x09,0x97,0x82,0xb9,0x5c,0x48,
++		0xc4,0x72,0xf9,0x88,0xd3,0x49,0x1c,0x7c,0x3b,0x3a,0x7a,0xe9,0xa1,0x63,0x26,0xb3,
++		0xc4,0x28,0xd5,0x60,0x47,0x00,0xa5,0xfe,0x47,0xdf,0xdd,0x1e,0x44,0xd5,0x24,0x9d, /* 1040 */
++		0x32,0x6b,0xf3,0xa1,0xf0,0xbc,0xf9,0xb1,0x1e,0x03,0xe8,0x27,0x16,0x84,0xa5,0xf7,
++		0x95,0x36,0x1c,0xaf,0x8b,0x4c,0x1c,0x1c,0xab,0xe1,0xbe,0x8d,0x1d,0x0c,0x8c,0x78,
++		0x43,0x8c,0x14,0xdc,0x67,0x41,0x5c,0x0d,0x76,0xe0,0xd5,0xbb,0x4c,0x33,0x1e,0xee,
++		0xa1,0xab,0x6b,0x4c,0x21,0x45,0x43,0x13,0xe2,0x63,0xd3,0x8c,0x31,0x28,0x52,0x3a,
++		0x26,0x5d,0xb3,0xf9,0x49,0xef,0xec,0x52,0xfc,0x13,0xec,0x78,0xcc,0x00,0xed,0x9f, /* 1120 */
++		0xe0,0xdf,0xb0,0x16,0x81,0x47,0x83,0x25,0xe3,0x63,0x5b,0x73,0x47,0x0b,0xb6,0x85,
++		0x1f,0xe4,0x6e,0x5d,0xa2,0xda,0xbd,0x6a,0x0f,0x4e,0x3a,0xc3,0x9d,0x88,0x6d,0xc8,
++		0xd4,0x82,0x4c,0x4a,0x9c,0x3b,0x01,0x0e,0xac,0xcb,0x1d,0xbe,0x2b,0x52,0x86,0x6b,
++		0xa4,0x29,0xeb,0x62,0x4c,0xd4,0x99,0x2a,0x87,0x16,0x7f,0xc7,0xe0,0xc0,0x38,0x27,
++		0x30,0x0b,0xfc,0x5e,0x30,0xca,0x1f,0x48,0xb8,0xec,0xb8,0x50,0xa4,0x96,0x05,0x91, /* 1200 */
++		0xa2,0x42,0x90,0x8e,0xaf,0x64,0x97,0xfe,0xd3,0x86,0x1e,0x8a,0x23,0x8f,0x9e,0xce,
++		0x65,0xfc,0x75,0xf8,0x95,0xcb,0x6d,0x2b,0x8f,0x08,0xb6,0xf0,0xf1,0xd2,0xf4,0x07,
++		0x21,0x68,0xb5,0x03,0x53,0xf3,0xcb,0xfd,0x18,0x42,0x2c,0x82,0xe8,0xd0,0xb7,0xf4,
++		0xe6,0x9b,0x73,0x8b,0xa3,0x16,0x22,0xac,0x78,0xc3,0x03,0xa7,0x1a,0x17,0xc6,0x0e,
++		0x9a,0x09,0xfa,0xb3,0x8c,0x96,0xfa,0x13,0x8a,0x16,0x21,0xe4,0x78,0x64,0x88,0xf0, /* 1280 */
++		0xa9,0x45,0xd6,0x73,0xaf,0x69,0x37,0x24,0x73,0x1f,0x39,0xff,0xd3,0xc7,0x32,0x2a,
++		0xe1,0xea,0x86,0xac,0x48,0x47,0x2e,0x49,0x87,0xe7,0xfc,0x1c,0x95,0xca,0x49,0xff,
++		0x5a,0xb5,0x5e,0x31,0x2f,0xed,0x66,0xc4,0xab,0x8d,0xe4,0xb0,0x2f,0x5b,0x47,0x82,
++		0xda,0xfc,0xa9,0xce,0x26,0xf8,0x19,0xc0,0xbd,0xf3,0x62,0x1e,0xac,0x13,0x7f,0x25,
++		0x82,0x1b,0xea,0xe6,0x7c,0x2c,0x3f,0x0f,0x6d,0x40,0xea,0xcc,0x15,0xbf,0x78,0x43, /* 1360 */
++		0xfe,0xad,0x21,0xca,0x82,0x21,0x02,0x16,0xe7,0x91,0xfe,0x07,0xb1,0x62,0x62,0xe0,
++		0x28,0x26,0xa3,0xea,0x8b,0x4f,0xe5,0xee,0x40,0x87,0xc9,0x27,0x7c,0x1e,0xaf,0x52,
++		0xc8,0xf5,0xd4,0xa9,0xa8,0x0e,0x32,0x9a,0x32,0x41,0x55,0x81,0x3b,0x06,0xd1,0x69,
++		0x26,0x6f,0x8a,0x31,0x7d,0x79,0xb6,0xe4,0x8e,0xeb,0x6a,0x83,0xd1,0x56,0xdd,0xe8,
++		0x02,0x82,0x57,0xd9,0xc1,0xe4,0x1b,0x82,0x09,0xd3,0x8c,0x1d,0x1b,0x9d,0x0c,0x3a, /* 1440 */
++		0x90,0x2d,0x7b,0xc6,0x54,0xc2,0x38,0x8e,0xc4,0xc1,0xf7,0x43,0x28,0x29,0x7e,0xfb,
++		0x79,0x36,0x7e,0x40,0x2e,0xa1,0xde,0xa5,0xe9,0x1f,0xb2,0x6b,0x61,0x2d,0x62,0xce,
++		0x95,0x46,0xf5,0x1d,0x47,0xd8,0x54,0x27,0x5c,0x39,0xb7,0xcd,0x24,0x44,0x4e,0xb7,
++		0xa5,0x82,0x1e,0xce,0x1a,0x46,0x4c,0x62,0x94,0x7f,0x38,0xa4,0x4f,0x8b,0x2b,0xf2,
++		0x70,0xcf,0xb2,0x4d,0xe3,0xa1,0xd6,0x8e,0xea,0xed,0x66,0x2a,0x68,0xda,0x11,0x18, /* 1520 */
++		0xb7,0x7e,0x87,0x56,0x45,0xd4,0xad,0xef,0xdd,0x97,0x6b,0xca,0x7a,0x95,0xd3,0xd3,
++		0x94,0xbc,0xeb,0x22,0x93,0x70,0xf1,0x0a,0xa2,0x53,0x1a,0xb8,0x20,0xe1,0x74,0xf3,
++		0xea,0x48,0xf7,0x98,0xf7,0xe2,0xf9,0x8a,0x18,0xfe,0xf8,0x99,0xac,0xee,0x2f,0x47,
++		0xdd,0xc5,0x37,0x40,0x7b,0xfa,0xcf,0x0b,0x6e,0xc4,0x23,0xc1,0x82,0x74,0x55,0x7c,
++		0xd0,0x36,0xe5,0xce,0x88,0x8a,0x98,0xdb,0x44,0xfd,0x4e,0xd8,0x08,0xcd,0x5b,0xbb, /* 1600 */
++		0x78,0x0a,0x21,0x88,0x51,0xcd,0x30,0x46,0xc1,0x25,0x1c,0xea,0x91,0xd5,0xde,0xd6,
++		0x6d,0x9e,0xe9,0x0e,0xc1,0x0d,0x9b,0xde,0xc3,0x33,0x36,0x88,0x76,0xef,0xb4,0x44,
++		0xf2,0xd2,0xf2,0x57,0xa7,0xdb,0x12,0x47,0x07,0x46,0x8d,0x0d,0x9c,0xf2,0x32,0x2b,
++		0xe4,0x6b,0xba,0x93,0x53,0x55,0x1d,0xcc,0x5b,0x65,0x5e,0x48,0x27,0xa5,0x2e,0xb7,
++		0x50,0xad,0x09,0x58,0xce,0x89,0xec,0x27,0x15,0x13,0x53,0xc3,0xc1,0x91,0xb0,0x63, /* 1680 */
++		0xcc,0xb1,0x78,0x43,0xcd,0xda,0xf5,0x7f,0x69,0x5c,0x14,0xb3,0x07,0x1a,0x5a,0xc0,
++		0xc3,0xa4,0x57,0x6a,0x73,0x61,0xc5,0xee,0x0d,0x9b,0x4b,0x39,0x4b,0xb8,0x49,0x85,
++		0xf8,0x47,0xa4,0x26,0x01,0xc5,0x89,0xc0,0x61,0x47,0x45,0xf4,0xd3,0xb3,0xce,0x12,
++		0x1d,0xf7,0x95,0x0d,0x28,0x7a,0xde,0x28,0xde,0x3a,0xb1,0xcc,0xf9,0x5e,0x59,0xee,
++		0x77,0x71,0x21,0x46,0xa8,0xa8,0x17,0x99,0x30,0x5c,0x2f,0x1c,0xd7,0x2e,0x2e,0x34, /* 1760 */
++		0xa9,0x5e,0x03,0xed,0x96,0x67,0x02,0xcc,0x64,0x84,0x68,0x74,0x79,0x3d,0x02,0xe8,
++		0xfa,0xca,0xe7,0x5b,0x49,0x7c,0x55,0x2a,0x35,0xb9,0x10,0xa8,0xc6,0xf0,0x44,0xf7,
++		0xeb,0x8c,0x65,0x19,0x90,0x85,0xbf,0x69,0x58,0x6e,0xb2,0x63,0xbb,0x0f,0x43,0xbc,
++		0x1a,0x05,0xf8,0x8f,0x0b,0xf4,0xbe,0x87,0x6e,0x51,0x87,0x90,0x22,0x7e,0x04,0x73,
++		0xd8,0x19,0x31,0x13,0xe2,0x2a,0xdb,0x8f,0x46,0x23,0x23,0xc5,0x3e,0x43,0x6e,0xfc, /* 1840 */
++		0x6c,0xf2,0x7a,0x3d,0xe5,0x7a,0x72,0xf6,0x6f,0x54,0xf2,0x39,0x6f,0xc1,0xc3,0xc6,
++		0x2f,0xa8,0x72,0xfd,0x5d,0x3e,0x22,0x62,0xfc,0x50,0x92,0xfc,0x8d,0xc5,0xdd,0x53,
++		0xb3,0x93,0x82,0x17,0x85,0x61,0xc8,0x09,0xc6,0xa1,0x05,0x21,0xa2,0xa4,0xd0,0xc3,
++		0x2a,0x1d,0x47,0x77,0xc6,0x6b,0x7b,0x20,0x9d,0xab,0xe8,0x89,0x40,0xb3,0x36,0xe9,
++		0xaf,0xa3,0x7d,0x80,0x2b,0x7a,0xa8,0x4f,0x1b,0xaa,0xae,0xde,0x0c,0x2d,0x35,0x1b, /* 1920 */
++		0x17,0xbc,0x2d,0x84,0x70,0xc6,0x1e,0x4b,0x76,0x31,0xd7,0xc8,0xf1,0xdc,0x68,0xd1,
++		0x8c,0xb5,0x65,0xda,0xd0,0x1e,0x2c,0xbd,0xc1,0xa7,0x6c,0x87,0x92,0xd1,0x82,0x4d,
++		0xf8,0x65,0x64,0xcf,0x2a,0x1b,0xf4,0x95,0x75,0x73,0x6d,0xe6,0xc6,0xe6,0x20,0x51,
++		0x10,0x73,0x61,0xd4,0xf6,0x1d,0xc1,0xf2,0x12,0xd8,0x6b,0x89,0xb5,0xc7,0x6d,0x3d,
++		0xf6,0x76,0xd2,0x55,0x10,0x17,0x50,0x36,0x68,0x7b,0x58,0xc9,0x73,0x64,0xd7,0x63, /* 2000 */
++		0x37,0xe6,0x55,0xb4,0x8c,0x2b,0x3b,0xbd,0x03,0xb0,0x8f,0xb1,0x28,0xca,0xb9,0x50,
++		0x71,0x12,0xe5,0x09,0x70,0x0b,0x08,0xc2,0x03,0x84,0x6b,0xf5,0x48,0x63,0x55,0xcd,
++		0x06,0x02,0xf4,0x9c,0x9f,0x0f,0xa3,0x18,0x59,0x6f,0xe5,0xb7,0xcf,0x02,0x96,0x7f,
++		0x72,0x55,0xef,0x73,0x39,0xaf,0xdf,0x9d,0x63,0x95,0x95,0xba,0x7c,0xce,0x79,0xbc,
++		0xd1,0x43,0xe9,0x3a,0x75,0xf5,0xdf,0xe9,0xa4,0xb5,0xbe,0x80,0xb2,0x69,0xb8,0x71, /* 2080 */
++		0x37,0x61,0xad,0x80,0xd4,0x1e,0x11,0xcc,0x7c,0x70,0x8f,0x5e,0x06,0x91,0x46,0x11,
++		0xa9,0xcb,0xc5,0xb8,0x38,0xca,0xa3,0xf2,0xd7,0x82,0xe1,0x80,0xe7,0x11,0x72,0x06,
++		0xa2,0x6c,0x5d,0xaa,0x58,0x13,0x94,0x24,0x49,0xde,0x8d,0x59,0x90,0xbe,0x12,0x90,
++		0xdc,0x6a,0x20,0x20,0x71,0xa5,0x00,0x98,0xe5,0x37,0x0a,0x96,0x48,0x58,0xdf,0x89,
++		0x6c,0x65,0xb0,0x7e,0xc8,0xcf,0x53,0x2c,0x9f,0x5e,0x38,0xdc,0x69,0x9e,0x16,0x5f, /* 2160 */
++		0x94,0x66,0x67,0x33,0x42,0xb4,0x9a,0x2f,0x04,0x57,0x3a,0xf6,0x1f,0x08,0x27,0x7c,
++		0xe9,0x87,0xa1,0x1e,0xb5,0xe8,0x73,0x68,0x80,0xc5,0x5a,0x82,0x10,0x0d,0x85,0xce,
++		0x52,0x11,0x33,0x98,0xa4,0x1d,0x5d,0x0c,0x5f,0x31,0xe0,0xab,0xa4,0xc0,0x12,0xcb,
++		0x9d,0x5c,0xb6,0xd6,0x9b,0xd7,0x1f,0xf5,0x00,0xf2,0x51,0x4c,0xa0,0x6e,0x4b,0x1d,
++		0x8a,0x15,0x1a,0xd6,0x90,0x97,0x37,0x69,0xa3,0x3f,0xc2,0xf5,0xcd,0xf1,0xc6,0xeb, /* 2240 */
++		0x6b,0x02,0x99,0x35,0xf2,0x72,0x35,0xc8,0x71,0x45,0x50,0x24,0x60,0x90,0x96,0x70,
++		0x80,0x16,0xe2,0xa7,0x81,0xa6,0xc6,0x05,0x05,0x54,0xe4,0xc4,0xea,0xcb,0x22,0xf2,
++		0xc7,0xeb,0x96,0xa8,0x04,0x0c,0x46,0x33,0xe5,0x06,0x5c,0xf2,0x93,0x53,0x1f,0x55,
++		0x1c,0x15,0xfd,0xf3,0xbf,0x98,0x30,0x8e,0x89,0x9c,0x6f,0x04,0x82,0x39,0x84,0x31,
++		0xa3,0x32,0x86,0x22,0x96,0x97,0x30,0xe4,0x3e,0xc3,0x04,0x87,0xbf,0x91,0x98,0x17, /* 2320 */
++		0x3d,0x1d,0x44,0x7a,0x76,0x07,0x0c,0x1f,0x17,0x26,0x5a,0xe4,0x5b,0xff,0x78,0x76,
++		0x0b,0x1f,0x20,0x26,0x3c,0x51,0x52,0x6e,0x75,0x7d,0x8d,0x8e,0x9e,0xa1,0xa3,0xa4,
++		0xbd,0xd0,0xd3,0xd4,0xea,0x19,0x26,0x2c,0x50,0x61,0x6b,0x71,0x8b,0x96,0x9c,0xa6,
++		0xae,0xb9,0xc5,0xe5,0xe9,0x32,0x41,0x48,0x56,0x5e,0x64,0xca,0xe0,0xe1,0xef,0xf5,
++		0xfc,0xff,0x1f,0x33,0x35,0x3a,0x44,0x5e,0x62,0x66,0x7e,0x9a,0x9e,0xb0,0xc2,0xcb, /* 2400 */
++		0xd4,0xd9,0xdb,0xe4,0xef,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		0x15,0x25,0x32,0x45),                                                            /* 2420 */
++	},
++	{ KEY_ML_DSA_65, 6, 55,
++	  chunk_from_chars(
++		0x30,0x34,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,
++		0x03,0x12,0x04,0x22,0x80,0x20),
++	  chunk_from_chars(
++		0x70,0xce,0xfb,0x9a,0xed,0x5b,0x68,0xe0,0x18,0xb0,0x79,0xda,0x82,0x84,0xb9,0xd5,
++		0xca,0xd5,0x49,0x9e,0xd9,0xc2,0x65,0xff,0x73,0x58,0x80,0x05,0xd8,0x5c,0x22,0x5c),/*   32 */
++	  chunk_from_chars(
++		0x30,0x82,0x07,0xb2,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,
++		0x12,0x03,0x82,0x07,0xa1,0x00),
++	  chunk_from_chars(
++		0xd2,0xfd,0x03,0xf3,0xa1,0xb7,0xf6,0x35,0xaf,0x9f,0x34,0xd5,0x80,0xa9,0x8f,0x52,
++		0x4c,0x73,0x5b,0xd5,0xba,0x23,0x55,0xdc,0x6e,0x03,0x5b,0xd2,0x17,0x65,0x58,0x0c,
++		0xbb,0x11,0x19,0x23,0xf1,0x94,0xa7,0xcc,0x8a,0x7b,0xb2,0xeb,0xc5,0xc0,0xe7,0x1a,
++		0xa6,0x37,0xcc,0x80,0x0e,0x61,0x03,0xb8,0x50,0xa5,0x39,0xb2,0xa3,0x9e,0x1b,0x6d,
++		0x71,0x3e,0x5d,0xb8,0x31,0x4c,0x9a,0xe1,0xf8,0xbf,0x8a,0x38,0xf0,0x6a,0xfb,0x9d, /*   80 */
++		0x73,0xb1,0x61,0xb0,0xff,0xe3,0xa4,0x89,0x17,0x06,0xae,0x26,0xd5,0x4f,0xfb,0x49,
++		0x6d,0xf8,0xdc,0x0f,0x19,0x83,0x50,0x95,0x00,0xc9,0xab,0xbd,0x28,0xe5,0x9b,0x3f,
++		0xcd,0xab,0xbd,0xad,0xab,0xd4,0x5e,0xc3,0x14,0x99,0x37,0x8b,0xde,0x84,0x9e,0x7c,
++		0x1f,0x19,0xb7,0x04,0x4d,0x67,0xe0,0x51,0x06,0xd7,0x13,0x6d,0x95,0x38,0x0d,0x56,
++		0x05,0xd4,0x46,0x5d,0x87,0x75,0x57,0x06,0x5d,0xf0,0xa7,0x5d,0x3c,0x28,0x54,0x2f, /*  160 */
++		0x40,0xfe,0xed,0x42,0xec,0x7e,0x28,0x06,0x37,0xb0,0x83,0xd9,0x88,0xbc,0xa5,0xf6,
++		0x39,0x4e,0x02,0x39,0x6c,0x46,0x76,0x18,0x4f,0xb6,0x33,0x18,0xda,0xfa,0xf5,0xbb,
++		0xdd,0xe0,0x0e,0x30,0x8f,0xe8,0x40,0x19,0xc2,0x34,0x0a,0x3f,0x3e,0x1c,0x08,0x65,
++		0x62,0x49,0x70,0x71,0x12,0x83,0x35,0x6a,0xe1,0x4b,0xd6,0xb9,0x4d,0x1c,0x9a,0xe1,
++		0x88,0xde,0x1a,0x8a,0x2c,0xa8,0x24,0xa8,0xea,0xe2,0xfe,0x6a,0xfb,0x38,0xd8,0x3a, /*  240 */
++		0x2d,0x99,0x99,0x6a,0xb2,0x1f,0xe3,0xe8,0x4c,0x0b,0xe6,0xb6,0xda,0x08,0x87,0x9b,
++		0x67,0x73,0x74,0xfa,0x7c,0x69,0x1b,0x13,0xd4,0x0f,0xa9,0xd4,0xcc,0x26,0xb2,0x28,
++		0x8d,0x5a,0x8c,0x9a,0x43,0x72,0x43,0x81,0x00,0x4d,0x61,0xb0,0xd5,0x7f,0xf4,0x00,
++		0x31,0x4c,0x8e,0x30,0xee,0x79,0x6a,0xf1,0x0f,0x7e,0xe2,0x1b,0xf1,0x3d,0x08,0x18,
++		0x04,0x65,0xab,0xc7,0x2e,0xdd,0xb0,0x80,0xc6,0xa0,0x71,0x84,0xe3,0xee,0xdc,0x47, /*  320 */
++		0xc1,0x9a,0xa7,0xf0,0x9d,0x1f,0x33,0x09,0xe1,0x83,0xa2,0xbd,0x9b,0x05,0x73,0xdd,
++		0xe4,0x74,0xa8,0x1b,0xa4,0xf7,0x8d,0x0c,0x52,0x3d,0x0c,0x04,0xf9,0x00,0x60,0xfd,
++		0x57,0x1a,0x35,0xc0,0x37,0xe0,0x79,0xc5,0xe2,0x10,0xd7,0x39,0x0d,0xf5,0x68,0xf2,
++		0xe2,0xf0,0x3c,0xe4,0x44,0x20,0xc8,0x2f,0x3f,0xe6,0x9e,0xb9,0xb4,0x8e,0xe9,0x09,
++		0x62,0xd6,0xb0,0xf2,0x44,0x40,0x64,0x8f,0x71,0xed,0xb2,0x41,0xee,0x65,0x66,0xfc, /*  400 */
++		0x1a,0x64,0xca,0xbf,0x66,0xbe,0x6f,0xec,0xbc,0xb1,0x38,0x7c,0x82,0xa7,0xbc,0x20,
++		0x2d,0x9e,0x36,0x79,0x98,0xe2,0xa2,0x91,0xaf,0x0c,0xd1,0x57,0x06,0x77,0xfe,0x8d,
++		0x63,0xa3,0x28,0x5a,0x2e,0xa6,0xeb,0x29,0xaf,0x9d,0xc1,0xae,0xc1,0xc3,0x6c,0x47,
++		0x06,0xb1,0x2b,0xaa,0x20,0x83,0x96,0x92,0xf2,0x86,0xa6,0xe0,0x32,0x14,0x68,0xf7,
++		0x47,0x93,0x45,0xc4,0xd5,0x2f,0xbd,0xb2,0xf0,0x67,0x25,0xb5,0x54,0xb8,0x9e,0x24, /*  480 */
++		0x92,0x61,0x26,0x81,0xac,0xeb,0xc6,0xc7,0xba,0xda,0x92,0x25,0x81,0x8d,0xbc,0x35,
++		0xd6,0x4c,0x22,0xc4,0x8b,0xff,0x80,0xa7,0x30,0xd0,0x71,0x6d,0xfa,0xc9,0x9d,0xfd,
++		0x5b,0x89,0x92,0x61,0x1d,0x0c,0x93,0xee,0x90,0xbd,0xb2,0x60,0x02,0x2a,0xfe,0x25,
++		0xd9,0x13,0xe0,0x6e,0xff,0xb5,0x9c,0xb1,0xf8,0xa6,0x0c,0xbf,0xa5,0xab,0x2f,0x45,
++		0x9a,0x16,0xf4,0x67,0xe9,0x89,0x52,0x5e,0x0a,0x37,0xeb,0xe5,0x6e,0x83,0x3f,0xde, /*  560 */
++		0x55,0xdb,0x9d,0x15,0x30,0xad,0xcf,0x45,0x84,0x6d,0xf2,0x81,0xe4,0x7c,0xaa,0x1e,
++		0x0a,0x27,0xef,0xde,0x21,0x07,0xd3,0x54,0xce,0xa0,0xf6,0xa4,0x54,0x69,0x2f,0x04,
++		0xcd,0x83,0x8e,0xbd,0xd4,0x6e,0x19,0x1e,0x5d,0x9c,0x11,0x83,0x9a,0x2c,0x3f,0x48,
++		0x8a,0x4f,0xc7,0xcd,0x26,0x5a,0x7b,0x5d,0x32,0xb0,0x8c,0xbd,0xbf,0xab,0x9d,0x2c,
++		0xcd,0x76,0x22,0x2c,0x8e,0xe3,0x7d,0xdc,0xbd,0x2a,0xa0,0x63,0xed,0x86,0x14,0x73, /*  640 */
++		0xa6,0x45,0x4c,0xae,0xa3,0x77,0x85,0x0b,0x1a,0x2b,0x9d,0xdb,0xbc,0xb3,0x74,0xfa,
++		0xb5,0xb1,0x2f,0x35,0x1c,0x8e,0x58,0x88,0x87,0x2e,0x5c,0xd1,0xf6,0x0a,0x4f,0xae,
++		0x1f,0xf8,0x37,0xd1,0x92,0xc2,0x2b,0xeb,0x41,0xee,0x6f,0xa3,0x92,0xfc,0xdf,0x45,
++		0x50,0xff,0x46,0xb5,0xce,0x90,0x6d,0x01,0x7e,0xf3,0x07,0x7d,0xf1,0x32,0x30,0x0d,
++		0x8b,0xbf,0xa9,0xbb,0x03,0xc7,0x5e,0x79,0xe2,0xf0,0x4c,0x28,0x4a,0xd0,0x6a,0x44, /*  720 */
++		0x39,0x96,0x49,0xc3,0xe2,0xa2,0xa8,0xd1,0xef,0xe9,0xb7,0xa4,0xe0,0xc2,0x71,0x04,
++		0x7a,0xb7,0x59,0x08,0xbf,0xf7,0xdf,0x9e,0x30,0xec,0xa5,0x47,0x74,0x5b,0xae,0x23,
++		0xa8,0x6f,0xf9,0xa8,0xb5,0x8c,0x25,0x38,0xb8,0x8b,0x86,0x64,0x01,0x07,0x69,0x02,
++		0xdc,0x5f,0x0b,0xd7,0x61,0x68,0x7b,0x49,0xea,0xfe,0x36,0xd3,0x50,0xcb,0xed,0xfd,
++		0xd3,0x6c,0x12,0x1c,0xf2,0x37,0x86,0xbf,0xcf,0x7e,0x47,0x07,0x64,0x96,0xea,0xb6, /*  800 */
++		0xbb,0xda,0x77,0x40,0x49,0xc2,0xeb,0xab,0xe2,0xde,0x99,0xc4,0xc2,0x4f,0x2d,0xb7,
++		0x36,0x84,0x01,0x5b,0x37,0x39,0x77,0x49,0x67,0x60,0xcf,0x9a,0xc2,0x3d,0x8b,0x62,
++		0x31,0x33,0xdb,0x2d,0xe1,0x0d,0x73,0xfa,0x6a,0xd1,0xc6,0xda,0xc8,0x43,0x4f,0x28,
++		0xc6,0xe2,0x51,0xce,0x72,0x93,0xcf,0xf3,0xf3,0xb6,0x1e,0xfc,0xb5,0xa4,0x35,0x12,
++		0x36,0x70,0xf2,0x98,0x46,0xa1,0x3d,0xf3,0xee,0x71,0x26,0x04,0x46,0x1f,0x1b,0xab, /*  880 */
++		0x8f,0x4e,0xbc,0x83,0x6d,0xe0,0x58,0x97,0x8a,0xe7,0x34,0x39,0x6a,0x98,0x08,0x1b,
++		0x35,0xcc,0x98,0x18,0x8a,0x86,0x94,0x9c,0x99,0x27,0x0d,0x47,0x09,0x85,0x4c,0x5b,
++		0x35,0xb1,0x7f,0x48,0xa3,0x73,0x13,0x4c,0x81,0x4c,0xc8,0xa0,0xf3,0xe2,0xfa,0x80,
++		0x7f,0x2a,0x91,0x85,0x30,0x90,0x78,0x64,0x77,0x82,0x82,0xd7,0x5e,0x03,0xa4,0x1b,
++		0x25,0x04,0xee,0xd8,0x16,0xa4,0x17,0xa3,0xac,0x6b,0xa1,0x60,0x80,0xc3,0x9b,0x73, /*  960 */
++		0x10,0x19,0x20,0x02,0xa7,0x28,0xf7,0xf2,0x03,0x95,0x00,0x9a,0x9e,0x16,0x76,0x7c,
++		0xe1,0x97,0x1f,0x5d,0xe7,0xd2,0x29,0xa5,0x06,0x13,0x36,0x9e,0x43,0x82,0x04,0x5a,
++		0x8e,0x81,0x90,0x1f,0x4d,0xba,0x81,0x02,0xf3,0xd4,0x13,0xfe,0x35,0xb3,0x26,0xa8,
++		0x74,0xf2,0x33,0xb7,0x19,0xa7,0x13,0x76,0x00,0xd3,0x5d,0x33,0xae,0xb6,0xb7,0x25,
++		0x96,0x24,0x08,0x3a,0xa9,0x68,0x73,0x0c,0x8f,0x78,0x29,0x2a,0xd2,0x8f,0x14,0xee, /* 1040 */
++		0xab,0xe6,0x60,0x83,0x59,0x84,0xfe,0x69,0xef,0x23,0xde,0xc8,0xc3,0x27,0xc0,0xeb,
++		0x0b,0x88,0x2d,0x58,0x7e,0x1e,0xc4,0x33,0xda,0x85,0xc9,0xfd,0x1e,0x0a,0x34,0x99,
++		0x4d,0xea,0x24,0x0c,0x85,0x44,0x52,0xd1,0x8c,0x30,0xf4,0x96,0xe4,0x9e,0xc9,0x04,
++		0xb6,0x02,0xe0,0xf5,0x06,0x2e,0xdc,0xda,0x03,0x28,0x0a,0x53,0xb4,0x31,0x35,0x74,
++		0xcc,0x2c,0x0d,0x54,0x71,0xbc,0x96,0x13,0xbd,0xfd,0x66,0x41,0xf5,0xbd,0x12,0x7b, /* 1120 */
++		0xab,0x5b,0x5e,0xb3,0xd4,0x99,0xa3,0x31,0x14,0x04,0x82,0x20,0xe8,0x19,0xf8,0xee,
++		0x12,0xca,0x92,0x2c,0x8f,0x17,0xd9,0xc9,0xf5,0x1a,0xd5,0xbd,0x68,0x83,0xb1,0x0e,
++		0x6a,0xa2,0x48,0x3b,0xa4,0x9d,0xc5,0x47,0xda,0x76,0x86,0x15,0x13,0x44,0xf4,0xe9,
++		0x09,0x9b,0x38,0xe4,0x30,0xb5,0x22,0x6b,0x05,0x98,0x32,0xcf,0x03,0xdb,0x48,0xfb,
++		0x02,0xdb,0xa4,0xe6,0x15,0x93,0xdc,0x45,0x76,0x36,0x04,0x91,0x89,0x0e,0x53,0xec, /* 1200 */
++		0x0e,0x6a,0xc7,0x3c,0xf3,0x2b,0x25,0xd8,0x23,0xb3,0x84,0x56,0xe2,0x86,0x50,0x5a,
++		0x54,0x1e,0x5a,0xee,0xe9,0x6b,0x19,0x14,0xf5,0xf7,0x66,0x87,0xce,0x2b,0x01,0x60,
++		0x22,0x7a,0xbe,0xd7,0x79,0x93,0x59,0x4b,0xcd,0x83,0x13,0x66,0x20,0x6d,0x75,0x71,
++		0x40,0x82,0xf1,0xc4,0x6f,0x1f,0x44,0x39,0xac,0x81,0xa5,0x7a,0xf3,0x1c,0x81,0xc5,
++		0x55,0x30,0x7a,0x07,0x0f,0xfa,0x94,0xe0,0x47,0x9b,0x78,0x4b,0xbd,0x88,0xa6,0x0c, /* 1280 */
++		0xd4,0xc7,0xcf,0xd9,0x4e,0x6a,0xfe,0x02,0xf6,0xb2,0x1f,0x72,0xaf,0x0d,0xcd,0x66,
++		0x09,0xd4,0x0c,0x96,0x5c,0x14,0xe5,0xf2,0x38,0x91,0x83,0xe5,0x3d,0xe9,0x30,0xf7,
++		0xde,0x1d,0x44,0x21,0x5c,0xf4,0x91,0x44,0x84,0x4e,0x8b,0x87,0xf7,0x8a,0x7f,0x13,
++		0x2a,0xef,0xe2,0x2b,0xe8,0x0b,0x4e,0x3a,0x05,0xee,0x3a,0x68,0xcc,0xf6,0x09,0xef,
++		0x44,0x04,0x74,0x02,0xe4,0x49,0x30,0x46,0xe6,0xf9,0xc7,0x67,0xff,0x8a,0x75,0xe2, /* 1360 */
++		0x8b,0x3c,0xe0,0x77,0xfd,0xe7,0xe7,0xee,0xd3,0x13,0xb5,0xbf,0x7e,0x46,0x01,0x27,
++		0xca,0x81,0x82,0xe9,0xbc,0x79,0x4c,0x0d,0xfa,0x73,0x0f,0xb9,0x20,0x08,0x05,0x75,
++		0xa7,0x51,0xb5,0xca,0xec,0x85,0xa1,0x09,0xb4,0x42,0x2b,0xa2,0x66,0x74,0x3f,0x0d,
++		0x03,0x2b,0xda,0x8f,0x1c,0xa6,0x24,0x8c,0xdb,0x91,0x75,0x30,0xdf,0x13,0x02,0xa5,
++		0xf8,0xc1,0x8d,0xc6,0x42,0xd5,0x24,0x78,0xc9,0x8c,0x12,0xa3,0xf1,0x6e,0xf2,0xb6, /* 1440 */
++		0x2b,0x4f,0x59,0xea,0x1b,0xb5,0x8d,0xe7,0xb6,0x5b,0x3c,0x71,0x53,0xce,0x6d,0xa5,
++		0xe4,0x95,0x07,0x46,0xf8,0x0e,0x08,0x7a,0x0e,0x35,0x86,0xd0,0x97,0x79,0x1b,0xf3,
++		0x6d,0xef,0x86,0x5d,0x68,0x59,0x1d,0x39,0xd0,0x90,0x37,0x73,0xee,0xa9,0x62,0x14,
++		0x7f,0x34,0x70,0x41,0x38,0xb5,0x4d,0xf7,0x92,0x4c,0xdd,0x8c,0x33,0x3d,0xb5,0xe1,
++		0xa4,0x09,0xcc,0xb2,0xb3,0x4e,0x2c,0x3c,0x8c,0x7f,0xdd,0x3f,0xd8,0xd0,0x12,0xcb, /* 1520 */
++		0xf3,0x82,0xaa,0xa8,0x5e,0x83,0xa1,0x2f,0x23,0x5a,0x2d,0x14,0x7d,0x03,0x5b,0x7b,
++		0x28,0xb3,0x4b,0x6f,0x57,0x94,0x9f,0x32,0x24,0x82,0xa7,0xd4,0xd3,0xb1,0x50,0x45,
++		0xc4,0x20,0xd5,0xad,0xdc,0x7f,0x0e,0x69,0xb4,0xdc,0x1c,0xba,0x58,0xb0,0x1d,0x87,
++		0x24,0x80,0xb0,0x6a,0x26,0x0d,0x82,0x7d,0x89,0x1b,0x13,0xc4,0xc5,0xca,0x50,0xc7,
++		0x48,0xde,0x3c,0x77,0x1b,0xe6,0x1e,0x9a,0xa1,0x70,0x16,0x5c,0xb0,0x1f,0x4b,0xf5, /* 1600 */
++		0xda,0x27,0xa7,0x79,0x1d,0x3a,0xd3,0xf6,0x26,0x7b,0x4c,0xb4,0xe6,0x1b,0x28,0xfa,
++		0x17,0x08,0x41,0x8d,0x93,0x2d,0xfc,0x41,0x61,0x88,0x0c,0x5d,0x3b,0x17,0xa9,0x66,
++		0x3a,0x90,0x61,0xfa,0x8f,0x18,0x04,0x31,0x58,0x50,0xfe,0x4e,0x73,0x06,0xc8,0x82,
++		0xb3,0x82,0x27,0xe8,0x67,0xf8,0x08,0x72,0xcd,0xc1,0x94,0x4d,0x47,0x26,0x15,0xea,
++		0x49,0x00,0xef,0x7d,0x27,0x0b,0x88,0x1d,0x41,0x30,0xf5,0x6c,0x5c,0xc9,0x80,0xd9, /* 1680 */
++		0x2a,0x47,0xad,0xa6,0x65,0x7e,0xb6,0xf3,0x7a,0x38,0x5d,0x2d,0x8c,0xc9,0x93,0xe1,
++		0x44,0x2e,0xb0,0x52,0x81,0x85,0x36,0x36,0x99,0x1e,0x34,0xaa,0xdc,0x68,0x95,0x4d,
++		0x04,0xe7,0xad,0xef,0x76,0xbf,0x88,0x0f,0x05,0x9b,0x0c,0xbb,0x55,0xd9,0x15,0xa4,
++		0xb1,0x23,0xe2,0xf1,0x33,0x9a,0x07,0x3c,0xbf,0xbc,0x40,0x9b,0xef,0xf6,0x40,0x0a,
++		0xe0,0x96,0xd5,0xae,0x18,0xec,0x42,0xcf,0xfa,0xd5,0xb4,0x98,0x0f,0xa3,0x5b,0xf0, /* 1760 */
++		0x34,0x13,0xad,0xb5,0xd7,0xe6,0x87,0x6a,0xc3,0x55,0xd1,0xc9,0xed,0x70,0xca,0x2b,
++		0x97,0x39,0x54,0xd1,0x2b,0x3c,0xdd,0x76,0xac,0x68,0x35,0xdb,0x96,0x00,0x3e,0xd8,
++		0xc4,0xe2,0x88,0xb7,0x1f,0xd7,0x7d,0xba,0xa7,0x63,0x57,0x20,0xe1,0x2a,0xe0,0xa3,
++		0x17,0xde,0x80,0x8c,0x66,0x4e,0x31,0x7f,0x55,0x27,0x57,0x91,0xf3,0x24,0x5c,0xa4,
++		0xfe,0x5d,0x4d,0x41,0x07,0x7f,0xc1,0x50,0xa6,0xe4,0x03,0xd5,0xa2,0x08,0xe4,0x6e, /* 1840 */
++		0xad,0xbe,0x8f,0x2c,0xfb,0x8a,0xf4,0x72,0xf4,0xa0,0xce,0xac,0x01,0x52,0x19,0x47,
++		0x8e,0x6b,0x86,0xc9,0x58,0xcf,0x86,0x52,0x5b,0x74,0x85,0xc1,0x73,0x4c,0x7e,0xf0,
++		0x0e,0x90,0x68,0x3f,0xff,0x5d,0xbd,0x0a,0x7d,0x41,0x3a,0x85,0x50,0x21,0x02,0x6a,
++		0x1b,0x32,0x01,0x3a,0x46,0x16,0xcb,0xcd,0x37,0x00,0xac,0xbc,0x70,0x5b,0xe3,0xef,
++		0xba,0x62,0x5c,0x69,0xa0,0x25,0x26,0x7b,0xce,0x9d,0x13,0x5e,0x3f,0x5b,0x5c,0xc8, /* 1920 */
++		0xc4,0x39,0x56,0x40,0x7e,0x84,0xb6,0x66,0x31,0x03,0xe2,0x9c,0x24,0x20,0x35,0x55,
++		0x1a,0xe7,0x97,0xf5,0x6c,0x63,0x74,0xbe,0x0c,0x79,0x8c,0x0c,0xf3,0x98,0xf1,0xed),/* 1952 */
++	  chunk_from_chars(
++		0x52,0x91,0x7f,0xc3,0x95,0xad,0xdc,0x96,0x57,0x77,0xc5,0x1d,0x59,0x1e,0x5b,0x49,
++		0xec,0x6b,0x0d,0x31,0x58,0x4e,0x20,0xaf,0x1e,0x0d,0x96,0x87,0xc9,0x1d,0xeb,0x6a,
++		0x24,0x60,0xf0,0x09,0x3d,0xdf,0x82,0x92,0x6d,0xeb,0x76,0x5e,0xce,0x2d,0xd3,0x0b,
++		0x79,0x7b,0xa6,0xb7,0xb9,0x79,0x90,0x99,0x94,0x6f,0xb9,0x41,0x69,0x6c,0x15,0x19,
++		0x47,0x82,0x65,0x13,0x0d,0xfb,0x54,0xd4,0x5f,0x88,0xf0,0xb1,0x92,0x19,0x01,0xf6, /*   80 */
++		0x71,0xaf,0x4a,0x0b,0x13,0xbd,0x18,0x7c,0xdd,0xd6,0x3f,0x6a,0x1c,0xde,0x01,0x56,
++		0x53,0x61,0x46,0xd2,0xaf,0xf0,0xd5,0x0d,0x14,0x64,0xda,0x3b,0x11,0x48,0x29,0x1d,
++		0xa9,0xc5,0xa4,0x18,0x8f,0xdf,0xea,0xe1,0xac,0x90,0x66,0xac,0x8c,0x10,0x40,0x2a,
++		0xb8,0x6b,0xb8,0xc5,0xcb,0xbf,0xc4,0xdc,0xd1,0xd6,0x78,0x38,0x80,0x27,0x9a,0xe8,
++		0x51,0xa6,0x00,0x9d,0x21,0x3c,0x6a,0x61,0x60,0x68,0x52,0xcb,0x29,0xa7,0xbe,0x77, /*  160 */
++		0x2c,0x1a,0xae,0x6a,0x6c,0xac,0x84,0x33,0xe9,0xff,0xd6,0xcf,0x67,0x89,0x74,0xe7,
++		0x20,0x52,0xb1,0xf3,0xf8,0x37,0x76,0x2e,0xd3,0xd8,0x40,0x00,0xc2,0x44,0x5b,0xe7,
++		0xfb,0x5a,0x46,0xaa,0x98,0xfb,0x85,0xb1,0xeb,0x2d,0xf2,0xfe,0xfe,0x59,0xff,0x2a,
++		0x46,0x0e,0x4a,0x88,0x31,0xde,0x3e,0x85,0x75,0x14,0x8a,0x70,0xb4,0x7b,0x6f,0xa8,
++		0x83,0x48,0x23,0x62,0x25,0xe0,0x86,0xbb,0xb8,0x97,0x96,0x4c,0xef,0xac,0xad,0xec, /*  240 */
++		0xaf,0xb2,0x2d,0x5a,0x99,0x64,0x84,0xb4,0x9a,0x66,0x88,0xcf,0xc8,0xf4,0x50,0xe3,
++		0xfd,0xc1,0xb6,0x74,0x61,0x8e,0x1d,0x1b,0xe5,0xf2,0x80,0x22,0x67,0x2e,0x4f,0xdd,
++		0x14,0x56,0xbb,0xb9,0xae,0x83,0x05,0x02,0xea,0x58,0x9f,0x81,0x88,0x5c,0x73,0x2a,
++		0xd6,0xe1,0x7e,0x4f,0x93,0x0c,0x9e,0xf1,0xe1,0xca,0x38,0x63,0x29,0xfc,0xab,0xec,
++		0x71,0x2a,0x07,0x1b,0x17,0xd7,0x3d,0x2b,0x42,0xa9,0x42,0xc3,0xac,0x0a,0xb6,0xac, /*  320 */
++		0x1f,0xb0,0x36,0xa3,0x39,0x59,0x85,0xcf,0xec,0xd7,0x9c,0xf9,0xd1,0x12,0x2a,0xe6,
++		0x74,0xac,0xe5,0x65,0xe6,0xf3,0x87,0xd0,0x58,0x02,0x31,0x13,0x45,0xf3,0xa1,0x1d,
++		0xf9,0x18,0x06,0x9c,0xc2,0x55,0x1c,0x35,0xd6,0xaf,0xfa,0xa1,0x3f,0xcd,0xd8,0x07,
++		0x6d,0xe4,0x7b,0x8c,0x26,0x63,0x57,0x42,0x07,0xff,0x96,0xcf,0xb5,0x08,0x8d,0x46,
++		0xfa,0x1b,0x77,0xa2,0xaf,0x96,0xca,0xde,0xf6,0xe2,0xbe,0xe0,0x1a,0xb2,0xa9,0x09, /*  400 */
++		0xb9,0xf2,0xf4,0xe1,0xd1,0xbc,0x85,0x48,0x29,0xf2,0xaa,0xc8,0x64,0x99,0x28,0xc7,
++		0x9f,0xc9,0xb4,0x00,0x25,0xea,0x3b,0x32,0x36,0x0b,0x56,0x2c,0x31,0x71,0x60,0x7b,
++		0x41,0x1b,0xae,0x65,0xa0,0x68,0xe7,0x7e,0xbc,0x70,0x24,0x1b,0xd9,0x0c,0xf9,0xab,
++		0x54,0x05,0xa8,0x39,0xa6,0x4a,0xfb,0xf1,0x4c,0x43,0x1b,0x06,0xa7,0x48,0x48,0x0b,
++		0x64,0x7d,0x71,0x9b,0x34,0x09,0x3a,0x2c,0xce,0x27,0xce,0xcb,0xeb,0x8b,0xf6,0xd5, /*  480 */
++		0x52,0xb0,0xba,0x3b,0xe4,0x1a,0x4a,0x25,0x45,0xbb,0x2f,0x5b,0xf7,0xfa,0x4f,0xda,
++		0x8e,0x54,0xc5,0x05,0x49,0x48,0x51,0xa6,0x17,0x46,0xa4,0xf2,0xf5,0x85,0xea,0xfe,
++		0xe3,0xd3,0xfe,0x49,0x05,0x1c,0x82,0xc0,0xc1,0x73,0x69,0xc6,0xe0,0x24,0x96,0xaa,
++		0xa0,0x74,0x97,0xf4,0x4c,0x9e,0x28,0x84,0x7f,0x13,0x59,0x17,0x1f,0x7f,0xdf,0x5d,
++		0x94,0x2d,0x58,0xe6,0xbc,0xce,0xa8,0xd2,0x5c,0x89,0xf4,0x5a,0x8d,0xc6,0x37,0xfb, /*  560 */
++		0xe9,0x86,0x44,0x54,0x94,0x1e,0xbd,0xf5,0x41,0xaa,0xf5,0xa4,0x7e,0x4e,0x59,0xff,
++		0x31,0xbc,0x53,0xa3,0xc3,0x20,0xe3,0x58,0xc3,0x3b,0xa0,0xb1,0x36,0xfc,0xd3,0x62,
++		0x7e,0x5d,0x03,0x54,0x46,0xce,0x0e,0x77,0x6b,0x2b,0x8d,0xf2,0x63,0x1a,0x13,0x21,
++		0x59,0xd9,0x20,0x04,0xb0,0xa8,0x1f,0x59,0x38,0x76,0xad,0x3f,0x54,0x7d,0x77,0x8d,
++		0xc3,0x14,0x7b,0xb2,0x91,0x1d,0x65,0x12,0x4a,0x38,0x86,0x56,0x08,0xf9,0xbd,0x34, /*  640 */
++		0x33,0x62,0x9c,0xf4,0xba,0x45,0x29,0x8c,0xa4,0x19,0x48,0x41,0x23,0x78,0x20,0xea,
++		0xf0,0xdd,0x2b,0x4b,0xa3,0x4d,0xe7,0xd9,0x6d,0x1a,0x8c,0x77,0x5b,0x99,0x77,0x82,
++		0x58,0x01,0x38,0x6d,0x91,0x30,0x22,0x03,0x91,0x76,0x54,0x82,0x37,0x22,0x90,0x4e,
++		0x40,0x5a,0x46,0xab,0xf6,0x11,0xd0,0xc0,0x52,0x49,0x81,0x98,0x37,0xcd,0xa1,0x35,
++		0xd8,0xf2,0x8c,0x9f,0x08,0x36,0x97,0xf9,0x1a,0x6e,0x79,0x5e,0x31,0x96,0x98,0xa4, /*  720 */
++		0x7d,0xd7,0x8a,0xa4,0xfe,0xae,0x4d,0x53,0xc6,0x87,0xd2,0xc6,0x92,0x32,0x79,0x4f,
++		0x09,0x50,0xdd,0xec,0x5a,0xe8,0x0e,0xbb,0x10,0xc0,0x5a,0x7b,0xca,0x14,0xdb,0x18,
++		0xd9,0x5e,0x05,0x39,0x7c,0x94,0x95,0x7c,0x86,0x21,0xfb,0x4d,0x5f,0x8f,0x29,0x4d,
++		0x2c,0x4c,0x02,0xbb,0x14,0x98,0xe2,0xd7,0x66,0xea,0x68,0xc7,0x71,0xb3,0x1d,0xcf,
++		0x14,0xf0,0xfe,0x62,0xdc,0xf6,0x12,0x19,0xa7,0xcb,0xfb,0x2a,0x15,0x25,0x6b,0x6d, /*  800 */
++		0x14,0x80,0x60,0x18,0x70,0x6d,0x01,0xac,0xed,0xaf,0x50,0x74,0xf3,0xc0,0x53,0x4d,
++		0x71,0x28,0xe4,0xd4,0xa1,0x3a,0x38,0xde,0x36,0x44,0x61,0x02,0xbb,0x91,0x45,0x60,
++		0x8b,0x2d,0xe1,0x79,0xdf,0x1f,0x8e,0xec,0x21,0x6c,0x87,0xd6,0x57,0x9d,0x87,0x58,
++		0x62,0x72,0xae,0x4e,0x05,0x5e,0xc4,0xc3,0x61,0x6a,0x4a,0x45,0x2f,0x94,0x1f,0xf8,
++		0x44,0x7b,0x6e,0xf6,0xb3,0xcd,0xca,0x74,0x45,0x9a,0x36,0x42,0xef,0xc7,0xd5,0xad, /*  880 */
++		0xda,0x8b,0xbd,0xe0,0x46,0xa8,0xc8,0xea,0xad,0xbe,0x79,0x9b,0x2d,0xf7,0x9c,0x9c,
++		0x2f,0xa2,0xb6,0xc6,0xa5,0xf0,0x0e,0x63,0x45,0x3d,0xd3,0xcf,0xb4,0x4c,0x68,0xac,
++		0x0d,0xbd,0xd1,0xe8,0x01,0xf3,0x38,0x1e,0x84,0x84,0x73,0x26,0x4a,0x87,0xbd,0xc8,
++		0xef,0x36,0x9e,0xb5,0x7c,0x74,0x43,0x7a,0x8d,0x18,0xb0,0xcb,0x49,0xe3,0x97,0xe2,
++		0x89,0x9a,0x52,0x67,0x62,0xdb,0x75,0x81,0x2e,0x7b,0x22,0x4c,0xc7,0xbc,0xa6,0xea, /*  960 */
++		0x99,0x84,0xfa,0xa8,0x42,0x0b,0x32,0xc7,0x64,0xe2,0x7e,0xf1,0xcf,0x6c,0x85,0xd9,
++		0x7d,0x20,0xb4,0x29,0x0a,0xb3,0x11,0xe9,0xe0,0x1a,0xe6,0xc9,0xd0,0x3f,0x71,0xd3,
++		0x22,0x78,0x53,0xcb,0x28,0xe4,0x01,0x21,0xb5,0xb8,0xa6,0x5e,0x37,0xa8,0x4f,0xab,
++		0xb9,0x7b,0x46,0x77,0xb4,0x0c,0x7d,0x92,0x04,0xc1,0xea,0x6c,0xb8,0xa7,0xf7,0x1a,
++		0x4b,0x70,0xd8,0x07,0x84,0xfa,0xeb,0x59,0x4e,0x6f,0x38,0x28,0x0f,0xeb,0x55,0xf7, /* 1040 */
++		0xfa,0x25,0x6f,0xd4,0x7b,0x6d,0xff,0x45,0x64,0xf4,0xb6,0x02,0x14,0x24,0xf4,0xac,
++		0x85,0xd9,0x89,0x5b,0x8b,0x4f,0x31,0xa5,0xb4,0x17,0x73,0x80,0x4d,0x69,0xad,0x79,
++		0x56,0xe9,0x00,0x3d,0xa6,0x90,0x65,0x0b,0x98,0x94,0xeb,0x4a,0xe2,0x41,0xcc,0x62,
++		0x0e,0xb7,0xd1,0xe0,0x94,0x74,0xda,0x19,0x32,0x8f,0xbe,0x32,0xf0,0x3f,0x17,0x12,
++		0x4a,0x99,0xbe,0x4a,0xed,0x74,0x20,0x10,0x2e,0x96,0x10,0xde,0xc9,0x82,0xff,0x93, /* 1120 */
++		0x1b,0xb0,0x55,0x72,0xdb,0x6b,0xa6,0x78,0x43,0x23,0x10,0xb7,0xd0,0x59,0x4b,0xcb,
++		0x62,0xb7,0xad,0x05,0x3b,0xb1,0xef,0x17,0x3f,0x0f,0xd9,0x29,0x89,0x13,0x90,0x61,
++		0x36,0x13,0x27,0x65,0x46,0x7e,0x9d,0x98,0x87,0xb0,0x87,0x43,0xdf,0xbe,0x3e,0x8d,
++		0x90,0x9b,0xf2,0xdc,0xa6,0x17,0x9a,0x68,0x7d,0x46,0x55,0x1e,0x6b,0x6a,0x56,0x27,
++		0x8e,0x60,0x7d,0x61,0x03,0xb8,0xbe,0x92,0x18,0x72,0x62,0x45,0x75,0xa1,0x90,0xc1, /* 1200 */
++		0x92,0x31,0x10,0xc2,0xa0,0x87,0xec,0xb4,0x0d,0x02,0xc2,0xa0,0x18,0x13,0x53,0x85,
++		0x35,0xc9,0xe0,0x09,0xa2,0xea,0x28,0x6d,0xdb,0x52,0x9a,0x52,0xbf,0xaa,0x1c,0x18,
++		0x94,0x55,0xc8,0x58,0x1e,0x81,0xcd,0x6e,0xb7,0xdd,0x68,0x90,0xd4,0x3a,0x8d,0x7f,
++		0xb6,0x5d,0x9c,0x04,0xa9,0x70,0x56,0x2e,0x2b,0x3d,0xb2,0xc3,0x16,0x3c,0xc2,0xc6,
++		0xab,0x26,0xe9,0x4b,0x7a,0x60,0x04,0x07,0x97,0x37,0x71,0x33,0xc9,0x82,0xbc,0x57, /* 1280 */
++		0x50,0xbd,0x9a,0xc8,0x24,0x56,0x98,0x5c,0x4d,0xb1,0x39,0xbf,0xab,0x64,0xbf,0xa2,
++		0x8a,0xc7,0xa0,0x43,0x26,0x18,0x73,0x68,0x56,0xdf,0xa8,0x92,0x90,0x5b,0x45,0x73,
++		0xcb,0x87,0x7d,0xd7,0x80,0x36,0xad,0xef,0xdb,0xe2,0x60,0x25,0x7e,0xff,0x85,0x64,
++		0xf8,0x77,0x92,0x2f,0xc0,0x5f,0xe8,0x88,0x18,0x20,0xb3,0x26,0x28,0xa1,0xc2,0x79,
++		0x8c,0xea,0xe5,0xa4,0x89,0x85,0xfc,0xd3,0x50,0xff,0x6c,0x99,0x83,0x4b,0xb8,0xdf, /* 1360 */
++		0xe1,0xdd,0xec,0x27,0x6d,0x44,0xb0,0x18,0x62,0x6b,0xcf,0x7b,0x18,0x53,0xdf,0x83,
++		0x2c,0x10,0xda,0x5a,0x67,0x35,0xf6,0x2d,0x3f,0x80,0xdc,0xfe,0xb1,0x59,0x3d,0x4a,
++		0xa4,0x98,0x26,0x15,0xb5,0x85,0x57,0xe0,0xc9,0x88,0xe1,0x96,0x8e,0xf5,0xa4,0xed,
++		0x3b,0x80,0xf2,0x08,0xcf,0xb2,0x31,0x7e,0x38,0xd1,0x33,0xe3,0xdf,0xa8,0x42,0x73,
++		0x28,0x16,0xfd,0x23,0x86,0xd2,0x2f,0xe6,0x97,0x35,0x8d,0xed,0x84,0x82,0xac,0x1d, /* 1440 */
++		0xa0,0x90,0x86,0x12,0xb3,0x97,0xd9,0x3d,0xcc,0x1c,0xe1,0xcd,0xa1,0xf0,0xdb,0x22,
++		0x8f,0x70,0x5c,0x7f,0xb8,0x33,0x89,0x34,0x27,0x94,0x6a,0x91,0x05,0x67,0x51,0x2f,
++		0x9b,0x88,0x5c,0xef,0x7c,0xaf,0x21,0xb2,0x82,0x6b,0x9e,0x27,0x20,0x2c,0x92,0xbe,
++		0x4d,0xcb,0x05,0xb4,0x75,0xbb,0x9a,0x09,0x36,0x4d,0x4e,0x29,0xe5,0x58,0x4c,0x98,
++		0x97,0x86,0xb1,0xa9,0xf5,0xb2,0x6b,0x18,0xb5,0x12,0x3e,0x63,0xc3,0xde,0x41,0x81, /* 1520 */
++		0x45,0x32,0xf1,0xf0,0xb2,0x6c,0x59,0x6e,0x55,0x7b,0x9d,0xf1,0xa6,0x2e,0x80,0xc4,
++		0x7d,0xec,0x10,0xee,0xf7,0xcd,0x6c,0x4a,0x04,0xa9,0x3e,0x15,0xf0,0xa9,0x63,0x9c,
++		0x7b,0x93,0xb8,0x5a,0x2c,0x21,0x06,0x45,0x63,0xc5,0x47,0x3a,0x74,0xce,0xe9,0x17,
++		0x2f,0x86,0x14,0x5a,0xb8,0xc4,0x0b,0x36,0xec,0x51,0x73,0x84,0xea,0xf1,0x87,0x88,
++		0x5f,0xa3,0x10,0xd5,0xc0,0xe6,0xe2,0x45,0xc0,0x33,0x4e,0x9c,0xad,0x63,0x0a,0xa5, /* 1600 */
++		0xf9,0xfb,0xe9,0xd7,0x3a,0x1a,0x0b,0x48,0xad,0x6f,0x58,0x43,0x98,0x69,0x6c,0x20,
++		0x20,0x89,0xa4,0x1d,0xbd,0xfc,0xb8,0x5a,0x82,0x08,0x01,0x26,0xbd,0x14,0x2c,0xa5,
++		0xa8,0x3b,0xe1,0x3c,0x26,0x4e,0xd6,0x42,0x69,0x77,0xd0,0x8e,0xba,0xff,0x35,0xba,
++		0x6e,0x2c,0x56,0x10,0x8b,0x67,0xf5,0x7c,0xcf,0xc8,0xcb,0x1b,0xed,0x96,0x4b,0x60,
++		0x43,0x66,0xbc,0x3a,0x25,0x63,0x6e,0xdb,0x00,0xe8,0xf7,0xcf,0x33,0x01,0xd0,0xd0, /* 1680 */
++		0xf0,0x7d,0x1f,0x45,0xef,0x77,0xaa,0x58,0xe7,0xa6,0x58,0x16,0xf5,0xbe,0xd5,0x70,
++		0x40,0x66,0x02,0x20,0x52,0x1d,0x93,0xf2,0x0c,0x26,0x72,0xfd,0x65,0x92,0x47,0x46,
++		0x7e,0x58,0x52,0xdf,0xb7,0xfb,0xa6,0xa7,0x97,0xaf,0x04,0x19,0xe3,0x32,0x8c,0xe0,
++		0x24,0x51,0xda,0x76,0x4b,0x36,0x01,0x79,0xac,0xb1,0xe2,0xb5,0xed,0x70,0x63,0x5b,
++		0x82,0x4a,0x7d,0xaf,0xeb,0xc7,0x94,0x1c,0xeb,0x81,0xf2,0xed,0x93,0x32,0xdf,0x7e, /* 1760 */
++		0x82,0x61,0x5e,0xa8,0xc2,0x5c,0xad,0xf1,0xe0,0xbc,0x11,0xab,0x71,0xfb,0xc0,0xcc,
++		0x07,0x94,0x85,0xef,0x55,0x15,0x94,0xf4,0x5c,0x6a,0xa4,0x59,0xe3,0x59,0x53,0x5c,
++		0xc3,0x6b,0x6f,0x55,0x22,0xc4,0xed,0xb9,0xae,0xad,0x82,0x53,0x8b,0x35,0xbc,0xf7,
++		0x05,0xd1,0x77,0x13,0xe4,0x27,0xd1,0x7a,0xb3,0xc0,0x10,0xee,0x38,0x84,0xf1,0xff,
++		0x84,0x1f,0x17,0x3a,0x00,0x7a,0x42,0x95,0x10,0x98,0x92,0x42,0x85,0xbd,0x4e,0xbb, /* 1840 */
++		0x7a,0x6c,0x1b,0x68,0x72,0x4f,0x8c,0xb6,0xea,0x63,0x17,0x73,0xe2,0x41,0x25,0x9a,
++		0x9b,0x33,0xc6,0xbf,0xb1,0x1c,0xd8,0x89,0x58,0x54,0x13,0xb2,0x3b,0xb2,0x17,0xe1,
++		0x3a,0x60,0x97,0xe4,0xba,0xf9,0x8e,0x5a,0x49,0x32,0x37,0x2d,0xa5,0xe4,0xa7,0x1c,
++		0x1d,0xd1,0xe2,0x53,0x39,0x6f,0xef,0x07,0x29,0xc4,0xa3,0x0e,0x9b,0xd6,0xf9,0xaf,
++		0x4c,0x88,0xaf,0x5e,0xf8,0x1e,0x83,0xb5,0xbf,0x8c,0x45,0xb4,0x13,0x14,0x6d,0xcf, /* 1920 */
++		0xce,0x2a,0x5c,0x67,0x39,0x60,0xd8,0x22,0xcf,0xe5,0x7a,0x85,0x07,0x61,0xc3,0xc8,
++		0x46,0xc5,0x2e,0x8c,0x4c,0x45,0x7e,0x26,0x72,0xd9,0x81,0x16,0x0c,0x32,0xe0,0x03,
++		0x11,0xbd,0xa0,0xfe,0xa4,0xbf,0xc6,0xa4,0x3c,0xf0,0x78,0x81,0xcd,0x2b,0x1d,0xfd,
++		0x68,0x95,0x9e,0xe8,0x0f,0x8e,0x14,0x01,0x62,0xa7,0xd3,0xd1,0x8f,0x91,0xbd,0x28,
++		0xb1,0x41,0xb5,0x4b,0xbc,0x1a,0x53,0x4d,0x3b,0x68,0x8f,0x39,0x01,0xc4,0xb3,0x94, /* 2000 */
++		0x1f,0xf3,0xf3,0x08,0xa0,0x05,0x6d,0x40,0xe6,0xe6,0x43,0xe3,0xf5,0xdc,0x5c,0xa8,
++		0x8c,0xdf,0xb4,0xe2,0x3b,0xd6,0x8b,0xe2,0x0e,0x57,0x86,0x58,0x46,0x09,0x03,0x96,
++		0x9d,0x18,0x5b,0x24,0xcb,0xe9,0xed,0x91,0x7c,0xe1,0x1d,0x09,0x33,0x0c,0x6b,0xb3,
++		0x63,0x05,0xba,0x72,0x1c,0x6c,0xad,0xfb,0x69,0x50,0x13,0x9c,0x89,0x4d,0xe5,0xf7,
++		0x00,0xf4,0xe1,0xd4,0xd7,0x08,0x24,0x8a,0x3e,0xd6,0x89,0x73,0x2d,0x98,0xc4,0x26, /* 2080 */
++		0x12,0xaf,0x1f,0x57,0xfc,0x9d,0x0e,0xf7,0xd7,0xa0,0x3e,0x3e,0xbc,0x97,0xea,0x30,
++		0x35,0x59,0xc5,0x4e,0xe8,0x3c,0x97,0x0d,0xd2,0x06,0xd5,0x4f,0x48,0x5d,0xcc,0x82,
++		0x4d,0x12,0x23,0x2f,0x62,0x05,0xba,0xa7,0x73,0x36,0x7a,0x81,0x65,0x5d,0xa4,0xff,
++		0x40,0x38,0x15,0x1e,0x0e,0x63,0x21,0xe5,0x84,0x96,0xd3,0x7b,0x12,0x1f,0x97,0xa2,
++		0xa7,0xfd,0xbc,0xd7,0xd7,0xcd,0x77,0xe0,0xc6,0xbe,0x87,0x8e,0xf9,0x50,0xad,0xa5, /* 2160 */
++		0x91,0xb4,0x75,0xde,0xe1,0x75,0x9f,0x02,0x84,0x8d,0x85,0xa8,0xc0,0x5d,0x1b,0x97,
++		0xfd,0x89,0x9b,0x3a,0x70,0x17,0x90,0xf0,0xd5,0x9a,0xd8,0x3d,0x8d,0xf0,0x48,0x85,
++		0x0f,0xf3,0x3e,0x9e,0x8a,0x8f,0x89,0x0e,0x21,0x80,0x7d,0x7e,0x33,0xe2,0xf2,0x58,
++		0xb1,0x0e,0x80,0x6f,0x16,0x4e,0x4e,0x67,0x30,0x46,0x95,0xfb,0x23,0x81,0x0f,0x1e,
++		0xe2,0xa9,0x80,0xcf,0xe3,0xb6,0x8f,0x24,0x51,0x3a,0x6f,0x79,0x16,0x56,0xf6,0x12, /* 2240 */
++		0x4c,0x1f,0x20,0xdd,0x57,0xda,0x02,0x14,0x07,0x42,0xcc,0x74,0xbd,0x50,0xbd,0xe9,
++		0x7f,0xbb,0xb3,0x73,0x2c,0xa3,0xed,0xce,0x6b,0xe0,0x13,0x4f,0x64,0x4c,0x0d,0xde,
++		0x38,0xec,0x91,0xd6,0x8b,0x9c,0xd9,0x16,0xb5,0xc1,0xc2,0xa9,0x67,0xa8,0x34,0x13,
++		0xc6,0x4e,0x59,0xe3,0x7a,0x1d,0x87,0x93,0x1b,0x6d,0x96,0x9f,0x60,0x17,0xf8,0x3b,
++		0xc1,0x2d,0x78,0x41,0x19,0xa0,0x5b,0xc3,0x4a,0x04,0xe8,0xb9,0x7d,0x26,0x52,0x9a, /* 2320 */
++		0x4b,0xcc,0x6e,0x60,0x0e,0x22,0x66,0x24,0x42,0x15,0x24,0x77,0x27,0x39,0x42,0x91,
++		0x38,0xcb,0x41,0xf1,0xfd,0x78,0xe4,0xd8,0x2b,0xb9,0x0d,0x79,0x4e,0xfd,0x0c,0x32,
++		0x40,0x5a,0x1f,0xcf,0x38,0x50,0xb9,0x9f,0xfc,0x56,0x49,0x4e,0xc1,0x11,0x56,0xa0,
++		0x55,0x0b,0xd5,0x59,0xeb,0x5e,0xed,0xe7,0x9d,0x77,0x12,0x15,0x1f,0xf9,0x41,0x8d,
++		0x60,0x84,0x93,0xca,0xad,0xfd,0x3d,0xa9,0xf4,0x66,0x03,0xbc,0x34,0xd2,0xcb,0xa2, /* 2400 */
++		0xed,0xd4,0x0e,0x38,0xfb,0x9b,0xac,0x9f,0x2f,0x72,0x3e,0xdf,0xe1,0xae,0x1f,0xde,
++		0x44,0x82,0x9f,0x4e,0x03,0xcd,0x78,0x17,0x8f,0xb8,0x08,0x10,0x6d,0xe6,0xab,0x67,
++		0xfa,0xc0,0x0f,0x9d,0x9b,0xd2,0xac,0xa5,0x8a,0x85,0x08,0x41,0xb5,0x0a,0x71,0x36,
++		0x77,0x64,0x55,0xe5,0x70,0x30,0x93,0x63,0x40,0xa7,0x0c,0x50,0x1f,0x38,0x82,0xd1,
++		0x21,0x77,0xf5,0x4b,0x85,0xec,0x37,0xcf,0x47,0x67,0xd3,0xf1,0x1f,0xf0,0xc6,0x6d, /* 2480 */
++		0xb3,0x78,0x56,0xac,0xe2,0xbc,0xf3,0xee,0x7e,0x20,0xea,0x93,0xe5,0xaf,0x57,0xa9,
++		0x6b,0x89,0x22,0xc6,0x9e,0xe9,0x19,0x91,0x69,0x1d,0xd1,0xbd,0x7a,0x4e,0x46,0xeb,
++		0x92,0x38,0x0c,0xea,0xdd,0x03,0x1f,0x9b,0xe0,0x27,0x1e,0x00,0xff,0x42,0x99,0x1f,
++		0x08,0xe6,0x2c,0x0b,0xb1,0x58,0x77,0xb2,0x46,0x1c,0x1e,0x39,0x7e,0xbd,0x07,0x18,
++		0xe2,0x4e,0x86,0x55,0x7b,0x09,0xaf,0x34,0xf9,0xae,0xe0,0xbf,0xe9,0xdb,0x1f,0xed, /* 2560 */
++		0x5c,0x1c,0x52,0x99,0x87,0xbe,0x41,0x79,0xbc,0x51,0x6a,0xfb,0x47,0x94,0xf4,0xa3,
++		0x80,0x1a,0xb2,0x21,0xaf,0xa1,0x38,0xdc,0x9a,0x4f,0xb0,0xbf,0xfb,0x52,0xca,0x7a,
++		0x79,0xa9,0x7f,0x8b,0xe3,0x7a,0xd3,0xeb,0x37,0xb2,0x46,0xf0,0x19,0x5d,0x57,0xb9,
++		0xc9,0x4e,0xc3,0x27,0x24,0xf1,0xde,0xdf,0xff,0x1a,0x79,0x99,0x11,0x42,0xd1,0xc6,
++		0xc4,0x75,0xf8,0x66,0xdd,0x9d,0xcc,0x6a,0xfe,0x58,0xd0,0x9a,0x4c,0x56,0xad,0xdc, /* 2640 */
++		0x7f,0xc6,0x2c,0xef,0x4b,0xc3,0x5a,0x18,0xab,0x8b,0xcc,0x97,0xcb,0xfe,0xda,0xca,
++		0xa9,0x59,0x6c,0xa1,0x28,0xa7,0xbc,0xbe,0x53,0xb2,0x40,0x24,0x17,0x05,0x53,0x63,
++		0x7b,0x19,0xbc,0x6d,0xc4,0x8f,0x47,0x37,0x6d,0x8b,0x21,0x60,0xb3,0x80,0xfb,0x07,
++		0xf5,0xd4,0x04,0xb3,0xf0,0x62,0x9f,0x81,0xf0,0x03,0x7c,0xd2,0x11,0x23,0x18,0x64,
++		0x20,0xd3,0x13,0x23,0xda,0x52,0xdd,0x9b,0xa1,0x05,0xe2,0xf9,0x48,0xce,0xf7,0x3c, /* 2720 */
++		0x7b,0x60,0x88,0x10,0x79,0xcb,0x29,0xa1,0xb4,0xb8,0x2a,0xde,0x34,0x46,0x33,0x72,
++		0x4d,0xd7,0xdd,0xef,0x03,0x88,0x2a,0x19,0xaa,0x9b,0xe5,0x32,0x39,0xdc,0xd6,0x29,
++		0x28,0x3f,0x39,0x1a,0x56,0xe3,0x1b,0x6b,0x94,0x75,0xf3,0xc6,0xf9,0xa7,0x4f,0xd1,
++		0xec,0xa3,0x79,0x24,0xe0,0xe0,0x4c,0x9d,0xd0,0x64,0x8b,0xbc,0xed,0xb3,0xb7,0xad,
++		0x22,0x3e,0x70,0xc0,0x55,0xf0,0x11,0xbd,0x90,0x1b,0xf3,0x07,0x12,0x79,0xb4,0x47, /* 2800 */
++		0xfe,0x6b,0xdf,0x75,0x9e,0x5e,0xcb,0x66,0x15,0x7b,0xd2,0xa0,0x65,0x72,0xe3,0xa3,
++		0x81,0x88,0x0e,0x4a,0x7c,0x6a,0xc5,0xb3,0xf9,0x6e,0x71,0x5d,0x4c,0x95,0x99,0x54,
++		0xb4,0x84,0x43,0xe9,0xa9,0x38,0xbe,0x87,0x9f,0x59,0x25,0x68,0x55,0xa3,0x23,0x16,
++		0xb7,0xa8,0xbd,0x5f,0xa2,0x9e,0x8c,0x33,0x65,0x0f,0xbd,0x21,0x49,0xdb,0x1d,0x7a,
++		0x38,0xe7,0x61,0xe1,0xf1,0x43,0xca,0xef,0x24,0x2f,0xae,0xbf,0xb4,0xe1,0x4d,0x42, /* 2880 */
++		0x1b,0x0b,0xd8,0x1c,0x6f,0x7a,0xfd,0xb8,0xb3,0xa6,0xa9,0xff,0x01,0x63,0x19,0x4c,
++		0x9e,0x8a,0xc3,0x8d,0xc8,0xa7,0x48,0x6d,0x16,0xd4,0x43,0x01,0xba,0xa6,0x62,0x6e,
++		0x6e,0x20,0xc5,0x25,0xac,0x8a,0x42,0x15,0xc1,0xd4,0xfb,0xda,0x9a,0x9a,0x09,0x89,
++		0x14,0xe8,0x02,0x0e,0x48,0xc5,0x6c,0x16,0xbf,0xcd,0xec,0x53,0xbe,0xbc,0x64,0x21,
++		0x6d,0xe3,0x7f,0x54,0xd6,0xe8,0x24,0xfd,0x33,0xab,0x7a,0x34,0x61,0xed,0x9b,0x6c, /* 2960 */
++		0xb9,0x50,0xf5,0xe2,0x88,0x67,0x43,0xfe,0x87,0x8a,0x4e,0x2d,0xde,0x95,0x89,0xb6,
++		0x68,0xa5,0x1e,0x78,0x33,0x8e,0x57,0xfc,0x28,0x04,0xd4,0x6e,0x3c,0x88,0x4c,0xd2,
++		0x13,0x91,0x9a,0xca,0x98,0x07,0x6e,0x05,0xe1,0x3b,0x34,0x8d,0x74,0xb5,0xdb,0xfe,
++		0xc3,0x3c,0x20,0x50,0x21,0x3f,0x12,0x09,0x49,0xb2,0x18,0xd9,0x9b,0xe4,0x7a,0xcc,
++		0x1a,0x0b,0x25,0x48,0xca,0x37,0x55,0xf7,0x22,0xa2,0xd7,0xa9,0x2b,0x12,0x06,0xb3, /* 3040 */
++		0x4d,0x98,0xe5,0x0e,0x20,0xd0,0x14,0xd6,0x57,0x26,0x13,0xc5,0x13,0x66,0x66,0xda,
++		0x87,0xe4,0xb2,0xff,0xb4,0xe9,0xa3,0x50,0x27,0x72,0x7e,0x75,0x0e,0x93,0x25,0x48,
++		0xd1,0x37,0x02,0xac,0x9f,0x6e,0x37,0x4c,0x64,0x2a,0xad,0xd8,0xb8,0x02,0xfc,0xf1,
++		0x47,0x3c,0xad,0xe0,0x56,0x81,0x7d,0x81,0x0a,0x85,0x57,0xee,0x4f,0xb3,0x44,0xa6,
++		0x2f,0x2d,0x29,0x73,0xdf,0x0d,0x21,0xff,0xc6,0xa6,0x15,0xc3,0x83,0xe2,0xa7,0xa9, /* 3120 */
++		0x07,0x90,0x78,0xb9,0xf9,0xf0,0xee,0xec,0xf5,0x41,0x65,0xe0,0x91,0x62,0x1a,0xf8,
++		0x14,0x6e,0xea,0x45,0xf8,0xcc,0xfb,0xcb,0x8e,0xe4,0x30,0x6d,0xda,0x72,0x56,0x50,
++		0x1e,0xd1,0xba,0xe3,0x31,0x12,0x4a,0xc5,0x3c,0x0b,0x3b,0x3f,0x86,0x3c,0xbf,0x5d,
++		0x81,0x61,0xba,0x00,0x4d,0xf2,0x6c,0x7f,0xd5,0x80,0xcd,0x13,0xdc,0x0a,0x15,0x30,
++		0x31,0x0e,0xab,0xa7,0xd5,0x1b,0xd0,0x31,0xc1,0x1f,0xb7,0xd6,0xb2,0xb1,0x9a,0x90, /* 3200 */
++		0xfa,0xf9,0xdc,0x21,0x30,0x4a,0x16,0x03,0x90,0x50,0x46,0x21,0x8b,0xa5,0x81,0x03,
++		0xda,0xf2,0xf0,0x0f,0x04,0x79,0x5c,0x9a,0xde,0x57,0xca,0x5b,0x4f,0x7c,0x9b,0xa5,
++		0x2e,0x31,0xa0,0x38,0xa8,0x26,0xdd,0x97,0xc5,0x52,0xb3,0x63,0xf0,0x20,0x60,0x15,
++		0x09,0x26,0x35,0x43,0x50,0x57,0x70,0x9b,0xb3,0xc6,0x45,0x54,0xa5,0xce,0x11,0x51,
++		0x5d,0x7d,0x92,0x72,0x75,0x7c,0xeb,0xf5,0x03,0x25,0x3a,0x41,0xb2,0xb5,0xdf,0xee, /* 3280 */
++		0x46,0x50,0x51,0xab,0xaf,0xb6,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x0e,0x13,0x18,0x20,0x27),               /* 3309 */
++	  chunk_from_chars(
++		0x95,0x29,0x38,0x65,0x91,0x5a,0x9b,0xb4,0x55,0x86,0xa6,0x57,0xca,0xc3,0xbd,0x5a,
++		0xd0,0x01,0xd0,0x57,0xc5,0xf8,0x4f,0x79,0x4a,0x17,0xeb,0x5a,0xf3,0xe0,0xcd,0xde,
++		0x1b,0xf5,0xb1,0x1e,0x4a,0x2c,0xc7,0x4e,0x9c,0x22,0xbb,0xbc,0xe6,0x5c,0xbb,0x0d,
++		0x17,0xc5,0x90,0xa2,0x44,0x08,0xe1,0x11,0x13,0x07,0x4c,0x27,0x11,0x83,0x38,0xa0,
++		0xfd,0xff,0x96,0x8e,0x14,0x0a,0x5a,0xf5,0x65,0xa0,0xd2,0x78,0xec,0xd4,0xd1,0xa4, /*   80 */
++		0x2a,0xa2,0x76,0x7a,0x55,0x7f,0x92,0x5e,0xbd,0x83,0x04,0x80,0x3e,0x16,0x00,0x11,
++		0x5a,0xc9,0xa5,0x8c,0x67,0x0e,0x3c,0x29,0xae,0xf8,0x37,0x54,0xbb,0xcd,0x31,0x29,
++		0x66,0xbf,0x5a,0x82,0x47,0xde,0x0b,0x12,0x6f,0x71,0x02,0x87,0x64,0xf0,0x79,0x4f,
++		0xcc,0xff,0x73,0xfc,0xfe,0x47,0x3c,0x19,0xec,0x32,0xfd,0xbd,0x28,0x59,0x61,0x17,
++		0xfc,0x23,0xbd,0xb0,0xa6,0x27,0x87,0x33,0x15,0x87,0x82,0x95,0x9f,0xdb,0xdb,0xa9, /*  160 */
++		0x55,0x0e,0x25,0x7b,0xcf,0xd9,0xcf,0xf8,0xcf,0xe5,0x3e,0x64,0xc6,0x70,0x80,0x99,
++		0x14,0x1f,0x5b,0x8d,0x25,0x78,0x68,0xae,0x7e,0x17,0x60,0x9e,0xa5,0x12,0xfc,0x1d,
++		0xfa,0x16,0xac,0xe8,0xc3,0x39,0x28,0x18,0xda,0x71,0xd0,0x1c,0x9b,0xeb,0x95,0x40,
++		0xf3,0x61,0x32,0x9a,0xb6,0xcd,0xb7,0x2c,0xad,0xf0,0xbb,0x0d,0x55,0xad,0x08,0xb0,
++		0xf7,0x18,0x39,0x39,0x17,0x91,0xad,0x77,0x87,0xbd,0xc8,0xfa,0x4b,0x33,0x7b,0xb3, /*  240 */
++		0x37,0x4c,0xd9,0xd4,0xce,0xc8,0xef,0x94,0x15,0xc6,0xf9,0xd7,0xf9,0x48,0x18,0xa8,
++		0x20,0xfe,0x59,0x0c,0x6d,0xb1,0x52,0x68,0xb4,0xb9,0xe5,0x08,0x66,0xea,0x96,0x85,
++		0x7c,0xdf,0xa5,0xe2,0x65,0x10,0x85,0xda,0xb1,0xb9,0x29,0x0c,0x2f,0xfd,0xc9,0xe1,
++		0xf3,0x71,0x4b,0x5b,0x84,0xc9,0x79,0xc5,0x11,0xf5,0x94,0xec,0xe2,0x4e,0x7e,0xee,
++		0x3d,0x1c,0x71,0x9a,0x52,0xcc,0x68,0xa4,0x0b,0xe8,0x5a,0xd9,0x1d,0x87,0x96,0x8f, /*  320 */
++		0xdf,0x52,0x1a,0xdf,0x29,0x4c,0xb8,0xf6,0x30,0x80,0x86,0x38,0x64,0x00,0x73,0x21,
++		0x93,0x46,0x54,0x5d,0xb3,0xc2,0x1a,0x4b,0x1c,0x86,0x10,0x73,0xef,0xc3,0x39,0x4a,
++		0x92,0x71,0xa5,0x22,0x14,0xb4,0x40,0xa6,0x9a,0xcf,0x93,0xaa,0x31,0x61,0x59,0x98,
++		0x0c,0x82,0xc0,0x85,0x08,0x3b,0xa8,0x6f,0xb6,0x35,0x3b,0x66,0x19,0xc8,0x72,0xfa,
++		0x03,0x74,0x49,0xa9,0xe8,0x30,0x8c,0x0d,0x6a,0x04,0xe2,0x76,0xa9,0x92,0xe8,0xae, /*  400 */
++		0x61,0x44,0x1a,0x7b,0x62,0x65,0x2f,0x31,0xa4,0x0c,0x72,0xbd,0x3c,0x7f,0xa4,0x9a,
++		0xf0,0x7e,0xbf,0x44,0x25,0x4a,0x1f,0x23,0xbf,0x1e,0xac,0x09,0xc1,0x6d,0xfd,0xcf,
++		0x43,0xe8,0x1b,0x76,0x00,0x6c,0x25,0xb9,0x56,0xcd,0x4d,0x9e,0x9f,0x59,0xb0,0x54,
++		0xca,0x69,0xe8,0x2a,0xfa,0xe3,0xf6,0x01,0xe5,0xd6,0x2b,0x8c,0x9b,0x84,0xae,0xdd,
++		0x31,0x15,0x14,0x57,0x6a,0x57,0xad,0xc0,0x76,0xb5,0xad,0x90,0xbb,0x14,0xbb,0x0a, /*  480 */
++		0x4d,0xea,0x36,0xf1,0xf0,0x15,0xb6,0x56,0xf4,0xfa,0x08,0x7c,0xee,0xd2,0xc1,0x14,
++		0x0d,0x1c,0xb4,0x44,0xc6,0x13,0x0e,0xa4,0xac,0xb8,0x6f,0x1e,0x42,0x64,0xd6,0x24,
++		0x77,0xe8,0x67,0x13,0x50,0x0e,0x22,0x4e,0xb3,0x53,0x23,0x86,0x33,0x62,0x70,0xa9,
++		0x0f,0xc4,0x23,0xca,0x3c,0xa7,0xf5,0x53,0x06,0xa3,0x73,0x05,0x8e,0x8c,0x21,0xdc,
++		0xbb,0x73,0x85,0x68,0xfa,0xc4,0xde,0xeb,0x72,0x17,0x2d,0x73,0x38,0x34,0x4d,0x83, /*  560 */
++		0xa7,0xc2,0x73,0x66,0xba,0x99,0x55,0x67,0xff,0x9a,0x71,0xdd,0xa2,0xc9,0xf8,0x2c,
++		0x1c,0xf6,0x7c,0x53,0xc2,0x47,0x58,0xe4,0x50,0x71,0xd1,0x2d,0xf6,0xe8,0x7c,0x0f,
++		0x31,0xf4,0x4b,0xf3,0x96,0x71,0xa4,0x2c,0xe1,0x90,0xfe,0x58,0xed,0x39,0x05,0x12,
++		0x13,0xcf,0xcf,0xb4,0x86,0x1b,0xd3,0xf1,0x0c,0xa9,0x05,0x42,0x6a,0xf0,0x07,0xd7,
++		0x06,0xe0,0x05,0x12,0x8d,0xa8,0x7a,0x5d,0xbd,0x9e,0x9b,0xdf,0x5f,0xf3,0x1f,0x60, /*  640 */
++		0x74,0x94,0x61,0xf0,0xfb,0x79,0xb6,0x88,0x96,0x4b,0xd1,0x22,0xb0,0x0e,0xcc,0xf5,
++		0x0c,0xd4,0x05,0x95,0x17,0x01,0xe3,0x38,0x67,0x15,0x7f,0xaf,0xef,0x4d,0xf3,0xe4,
++		0x83,0xda,0xb3,0x4c,0xbd,0x98,0x1c,0xfe,0x27,0x12,0x74,0x44,0xbd,0x9c,0xa5,0xa8,
++		0x5a,0x39,0xe8,0x9d,0x6e,0x42,0xf0,0x33,0x2a,0x4c,0x72,0xdd,0x65,0x60,0x17,0xa4,
++		0x16,0xda,0x2a,0xd6,0xd7,0x49,0x30,0x5e,0x78,0x6d,0x29,0x42,0xf2,0xdd,0x2c,0x81, /*  720 */
++		0xd5,0xde,0x57,0x92,0x8e,0xbb,0xf4,0x51,0x5c,0xe4,0x9e,0x4c,0x41,0xdb,0x33,0x79,
++		0x32,0x3c,0x8e,0x0f,0x62,0x3b,0xf9,0xa1,0x46,0x0c,0x80,0x90,0xb6,0x54,0x05,0x58,
++		0xfb,0x78,0x6f,0xeb,0xfa,0xba,0x16,0x9c,0xa0,0x17,0x30,0x14,0x6a,0x7a,0xd1,0xe3,
++		0xb8,0xe3,0x78,0xc5,0x3c,0xa3,0x28,0x79,0xe5,0x4b,0xee,0x74,0x05,0x9c,0x75,0x29,
++		0xe2,0x6e,0xbd,0x8c,0xe6,0xd1,0x8b,0x68,0x20,0x31,0xd3,0xdf,0xd8,0xb0,0xa1,0x7c, /*  800 */
++		0x26,0x85,0x3e,0x4e,0xd9,0xb5,0xb6,0x40,0x51,0x0e,0xd4,0xd0,0xfa,0xee,0xb3,0xe3,
++		0xaf,0x64,0x73,0x7a,0xbd,0xd8,0x6c,0x5b,0xc7,0x92,0x57,0xe6,0xd2,0x76,0xd5,0x5c,
++		0xb7,0x42,0x8b,0xa5,0x01,0x02,0x92,0xcd,0xf8,0x42,0xc1,0x35,0x00,0xf7,0x0c,0x42,
++		0x3c,0x27,0xbc,0x5a,0x98,0x9f,0xfc,0x42,0xff,0x59,0x05,0x93,0x12,0x0f,0xbe,0x06,
++		0xa4,0x73,0x21,0x48,0x14,0xf6,0xde,0xe5,0x18,0x20,0xcc,0xf1,0x42,0x46,0x69,0x58, /*  880 */
++		0x51,0x97,0xed,0x61,0x12,0x58,0xc0,0xf8,0x48,0xed,0x07,0xb9,0xf6,0x54,0x71,0x21,
++		0xa5,0xad,0x9d,0xa6,0xec,0x80,0x11,0x77,0x35,0xdf,0xc2,0x56,0xcf,0x02,0xf4,0xb3,
++		0xd8,0xe2,0x0d,0xb1,0x9f,0x00,0xfd,0x3c,0xbe,0x6b,0xbe,0x5c,0x5d,0xce,0xa9,0xea,
++		0xd8,0x3c,0xb9,0x0a,0x6a,0x10,0xd8,0xcd,0x35,0x29,0x49,0xf9,0x02,0x7f,0xf6,0x46,
++		0x81,0xc5,0xf8,0x0e,0x2c,0x8c,0xeb,0xe2,0xca,0xab,0x67,0xc7,0x86,0x68,0xaa,0xfa, /*  960 */
++		0xe5,0x78,0xf0,0x33,0x2b,0x84,0x42,0x91,0x29,0x56,0x3e,0xfd,0x7d,0x68,0x6a,0xa1,
++		0x16,0x4d,0xb6,0x39,0xaf,0x31,0x35,0xd4,0x55,0xe2,0xf5,0x12,0xad,0x40,0xcc,0xa6,
++		0x95,0xff,0xc1,0x9c,0x94,0x0c,0x9f,0xee,0x40,0x4d,0xed,0xe7,0xf8,0xe7,0xab,0x19,
++		0x51,0x9d,0x03,0xd8,0x6c,0x87,0x52,0xa7,0x5d,0xe0,0xb6,0x06,0x2d,0x8c,0x3b,0x5d,
++		0x4f,0x16,0x9f,0x96,0x47,0x2c,0x4c,0xb0,0x6a,0x24,0xd1,0xde,0x21,0x9f,0x6f,0x3c, /* 1040 */
++		0x32,0x42,0xc1,0x89,0xda,0xa7,0x01,0xe6,0x8c,0x03,0x83,0x4a,0x86,0x2e,0xe4,0x0d,
++		0x39,0x8b,0x97,0x31,0x1f,0xee,0x84,0x3f,0xef,0x51,0x5c,0x5b,0x56,0x8d,0xcb,0xfc,
++		0xcf,0x16,0x72,0x7d,0x4e,0x30,0xe2,0x95,0x98,0x23,0x0e,0x0d,0xf8,0x75,0xe3,0xcb,
++		0x9a,0xcd,0x63,0xdb,0x44,0x6b,0x3c,0xf8,0xa3,0x6c,0x63,0x64,0x04,0x8e,0x01,0x53,
++		0xbe,0xb8,0x83,0xa8,0x2a,0x45,0x84,0x0e,0xf5,0xcd,0x6d,0x69,0xfc,0x68,0x9d,0xc7, /* 1120 */
++		0x7c,0x41,0xc2,0xa6,0x3a,0x54,0x30,0x75,0x4e,0x4b,0xd6,0xa9,0xea,0xc0,0xa2,0x56,
++		0xf8,0x59,0xf8,0x5b,0xd0,0x03,0x7d,0x1d,0xb2,0x9f,0x0c,0xe0,0x34,0xbe,0x4f,0x45,
++		0x42,0xb1,0xaa,0xe6,0xbc,0x51,0xec,0x07,0x43,0xed,0x10,0x72,0x5e,0x49,0xf6,0x21,
++		0xc0,0xa6,0xd1,0x54,0x49,0xaf,0x72,0x4b,0xcb,0x7c,0x5b,0xd9,0x71,0x10,0xc9,0xc5,
++		0xe9,0x71,0xb8,0x4f,0xc9,0xda,0x43,0x32,0x33,0x23,0x2b,0xce,0xaa,0xda,0xda,0x02, /* 1200 */
++		0x6d,0xbf,0xcf,0x45,0x3d,0x3b,0xb5,0xbe,0x2a,0xa1,0x06,0x9b,0xef,0xbc,0xa4,0xec,
++		0x2d,0x07,0x38,0x61,0xc5,0x10,0x33,0xc6,0x6b,0xf6,0x14,0xf5,0x04,0xfc,0x55,0xed,
++		0xc7,0xd0,0xf9,0x01,0xfc,0x3b,0x5b,0x69,0x04,0x58,0xf7,0xbd,0xa8,0x76,0xb5,0x32,
++		0x65,0x31,0xa2,0x7f,0xe5,0x7f,0xc3,0xe4,0x2e,0x64,0x1f,0x03,0x1e,0xc4,0x49,0x45,
++		0xa4,0x77,0x6a,0xb9,0x77,0x9b,0x3a,0x00,0x7d,0xd0,0x0e,0x20,0xae,0xa3,0x02,0x1e, /* 1280 */
++		0xad,0x4e,0xee,0x2f,0x14,0x48,0xa0,0x21,0x59,0x1d,0xf7,0x5f,0x8f,0xbf,0x21,0xfb,
++		0x7e,0x5d,0xa1,0x5c,0x6b,0x38,0x51,0xb2,0xbd,0xa7,0x12,0xc4,0x72,0xbb,0x35,0xa2,
++		0xcf,0x75,0xac,0x77,0xfd,0x89,0xa4,0xd6,0xfe,0x66,0x2a,0x77,0x17,0x6b,0x03,0x22,
++		0x52,0x86,0xd3,0xa1,0x91,0x27,0x63,0x8d,0xa9,0x3f,0x4c,0xea,0x64,0x1c,0x49,0x87,
++		0xc2,0x74,0xd9,0xf8,0xb4,0x03,0x7d,0xdc,0x01,0x2a,0x14,0x5f,0x7e,0xa5,0x05,0x95, /* 1360 */
++		0x5a,0x55,0xd2,0x9d,0x22,0x5f,0x46,0xa4,0xe3,0xa8,0x8b,0x50,0x95,0xd8,0x81,0x02,
++		0x5f,0x6f,0x50,0xfb,0x8c,0x29,0xef,0xe3,0x91,0x20,0xdf,0x11,0x80,0xa9,0xfd,0x5f,
++		0x1d,0xec,0x71,0x26,0x35,0x4d,0xdf,0x0d,0x46,0x94,0xe6,0x46,0xc6,0x6c,0x93,0x25,
++		0xee,0x06,0xe8,0x8e,0x5a,0x4c,0xa3,0x49,0xd4,0x61,0x01,0xee,0x7f,0x91,0x9b,0x2f,
++		0x87,0xba,0x5d,0x1e,0x51,0x38,0x61,0x7a,0xb5,0xdc,0x9c,0x6b,0xac,0x70,0xeb,0x6b, /* 1440 */
++		0xbc,0xf4,0xdb,0x71,0x99,0xd1,0x78,0xab,0x44,0x52,0x32,0xd1,0x7a,0xb1,0x82,0xa0,
++		0x3a,0x5a,0x97,0x1c,0xdf,0x75,0x89,0x80,0x47,0x4c,0x52,0xe4,0xca,0x75,0xcf,0xc5,
++		0x38,0xed,0xff,0xad,0x26,0x9f,0xa2,0x0f,0x66,0xca,0x9d,0xe9,0xcd,0xfd,0x4a,0x9b,
++		0x2c,0x26,0xf0,0x8e,0x6f,0xba,0x1b,0xb9,0x85,0x70,0x87,0x51,0xa6,0x36,0xb4,0xe5,
++		0x93,0x8b,0xef,0x1f,0x61,0xad,0xb7,0xb3,0xf8,0xde,0xcb,0x43,0xf9,0xe6,0x79,0xae, /* 1520 */
++		0xbe,0xd1,0xa7,0x57,0xee,0x96,0xf2,0xb5,0x31,0x31,0x08,0x1d,0x18,0xac,0x85,0x18,
++		0xe2,0xf1,0xeb,0xe1,0xda,0xf1,0xca,0xb8,0x93,0x45,0x70,0x96,0xe4,0xe7,0xc8,0x05,
++		0xcb,0x48,0xf5,0x1b,0xdf,0xf5,0xb6,0x3e,0xc8,0xdf,0x24,0x2c,0xfe,0x3a,0x5a,0x13,
++		0x02,0xf3,0xf5,0xde,0x5a,0xef,0x84,0xfd,0x4a,0xf3,0x26,0xb7,0xc8,0x1b,0xcf,0xe2,
++		0xd9,0xf7,0xeb,0x55,0x8a,0xf3,0x6b,0xb5,0xac,0x62,0x2c,0x5e,0xef,0x42,0x59,0xbd, /* 1600 */
++		0xc5,0xcc,0xe3,0xe3,0x90,0xdc,0xcd,0xb9,0x4d,0x4c,0x0c,0x38,0x1f,0xb9,0xbe,0x7e,
++		0x70,0x32,0x21,0xce,0x3a,0x25,0xb7,0x75,0x06,0x82,0x8a,0x58,0x70,0xe1,0xaf,0x9d,
++		0x51,0xc3,0xb4,0x34,0xb7,0x0f,0x02,0x85,0xb8,0x03,0x84,0xe5,0x38,0xb9,0x44,0x4a,
++		0x61,0xe6,0x68,0x66,0x47,0x79,0xe0,0x51,0xef,0x90,0x7d,0xb9,0x9d,0xce,0x88,0xe6,
++		0x58,0xf1,0x50,0xd6,0x1d,0xf5,0x7f,0x18,0xc7,0x67,0xab,0x4a,0x9b,0x03,0xe9,0x0a, /* 1680 */
++		0x4e,0x23,0x24,0x97,0x69,0x01,0x49,0x20,0xf8,0x7f,0x1e,0x49,0x70,0x27,0x71,0xf1,
++		0xb4,0x44,0x76,0x81,0xc5,0xdd,0xd7,0x69,0x5b,0x99,0xfc,0x51,0x0e,0xe7,0x04,0x89,
++		0xca,0x3b,0x55,0x5f,0x21,0x78,0xcc,0xae,0xdd,0x6d,0x91,0x34,0x73,0x71,0xef,0x03,
++		0xb7,0x65,0xbd,0xb9,0x5a,0x63,0x52,0x58,0x4c,0xa6,0x75,0xb1,0xbe,0x52,0x1e,0x96,
++		0xd3,0xe6,0x5d,0xd7,0x2c,0xcf,0xb3,0x03,0xf4,0x83,0xad,0x7f,0xaa,0x1c,0x4c,0x72, /* 1760 */
++		0x17,0x6a,0x0a,0x45,0x20,0xb5,0x09,0x35,0x4b,0xd8,0xce,0xa0,0xeb,0x3a,0x5f,0xd7,
++		0x11,0x45,0x68,0x98,0x26,0xf4,0x9b,0x89,0x6f,0x34,0x43,0x64,0x51,0x08,0x94,0xf3,
++		0xee,0xe7,0xe3,0xa6,0x65,0xd8,0x2d,0x6e,0x72,0xdf,0xea,0x61,0xc5,0x0a,0x6e,0x0e,
++		0xe6,0xbf,0xaf,0xc4,0x5d,0x5f,0x6b,0x8d,0xae,0x16,0x02,0xc7,0x92,0x9c,0xfb,0x73,
++		0x42,0x84,0x2b,0x78,0xc4,0x5c,0xcf,0x86,0x87,0xfe,0xd5,0x4a,0x22,0x28,0x7d,0xa6, /* 1840 */
++		0x7a,0x2a,0x8d,0xfb,0xb4,0x54,0x9a,0x95,0x69,0xdd,0xa5,0xdf,0x53,0x02,0x4a,0x31,
++		0x30,0x28,0x04,0x4c,0xc3,0x9a,0x2b,0xce,0xf8,0x03,0xe0,0xf2,0x6c,0x68,0x77,0x58,
++		0xc4,0xcf,0x30,0x47,0xad,0x85,0x48,0xa8,0x91,0x6d,0x0d,0xf3,0x14,0x99,0xe8,0xdd,
++		0x74,0x71,0x8b,0xe5,0x90,0x89,0x5b,0x43,0x87,0x6d,0xd1,0x44,0xf2,0x9f,0x58,0x0d,
++		0xa2,0x29,0xdd,0x77,0xd5,0xbb,0xfc,0xfc,0xbf,0xa0,0x9d,0x9e,0x43,0x89,0xf5,0x4d, /* 1920 */
++		0xa9,0x4d,0x6d,0xec,0xd0,0x4c,0xb6,0x2f,0xa2,0x71,0x86,0x89,0x66,0x06,0xd7,0xf5,
++		0xb6,0x92,0x35,0x32,0xf9,0x4d,0x80,0x8c,0x5e,0xb2,0x63,0x2a,0xb4,0x3c,0x23,0xac,
++		0xa7,0xbb,0x78,0x0a,0x7f,0x59,0x0c,0x6e,0xb0,0xb9,0x38,0xdf,0x3e,0x84,0x6d,0xe5,
++		0x8c,0xe4,0x7b,0xfc,0x0d,0xf4,0xd9,0x20,0xb0,0xd2,0x6d,0x49,0x47,0xc1,0x57,0x28,
++		0x20,0xdb,0xd4,0x26,0x3e,0xcf,0x99,0x9e,0xc4,0xf9,0xd6,0xa4,0x44,0x38,0x38,0xeb, /* 2000 */
++		0x9a,0x71,0x4f,0x70,0xba,0x2c,0x71,0xb7,0x17,0xc8,0x96,0x7b,0x68,0x08,0x11,0x17,
++		0x19,0xaf,0x7d,0x81,0x16,0x44,0xd1,0x63,0x91,0xe1,0x91,0xa1,0x14,0xf6,0x6b,0xf2,
++		0x3a,0xd6,0xc6,0xb9,0x22,0x08,0x33,0xa9,0x7d,0xb4,0xbd,0xa9,0x1d,0x1d,0xc6,0x5b,
++		0xbb,0x03,0xa4,0xc2,0x06,0x32,0xda,0x0e,0xb9,0x9d,0x2d,0x26,0x10,0x60,0x28,0x16,
++		0xf4,0x5f,0x41,0x55,0x26,0xd0,0xbc,0xd0,0xf1,0x0a,0xb1,0x64,0x79,0xf1,0x99,0x66, /* 2080 */
++		0x33,0x02,0xf1,0xd6,0xec,0x95,0x38,0x5d,0x9e,0x98,0x29,0x3b,0x91,0x27,0x39,0xf3,
++		0x26,0x9d,0x8a,0x76,0x6c,0xd3,0xa3,0xe1,0x85,0x91,0xfb,0xdd,0xfb,0xcc,0x32,0x1a,
++		0x04,0x1d,0x7b,0x9e,0xe6,0xea,0x0e,0xa0,0xcd,0xb8,0x1c,0xe4,0xa9,0x8f,0x76,0xc6,
++		0xd5,0x7a,0x94,0xec,0x5e,0xcb,0xd4,0x25,0x94,0x2a,0x23,0xde,0x2e,0x48,0xd5,0x39,
++		0x7d,0x1f,0x79,0xdf,0xf9,0x76,0x49,0xfc,0xc2,0x00,0xa7,0xe6,0x74,0xa0,0x34,0xf3, /* 2160 */
++		0x57,0xc0,0x1e,0x4d,0x8f,0xfe,0x48,0x6e,0x0a,0x2f,0x27,0x78,0x53,0x67,0xe3,0xb3,
++		0x5b,0x68,0x66,0x58,0x4b,0xa0,0x76,0xf9,0x98,0x7e,0xfe,0x4a,0xe2,0x54,0x41,0x51,
++		0x4e,0x0e,0x98,0xa6,0x77,0x6d,0xe4,0x5e,0xc5,0xb1,0xec,0x9a,0x35,0x4f,0xcd,0xd2,
++		0x25,0xec,0xcb,0xfc,0x14,0xd8,0xd9,0x6b,0x79,0xa0,0x8d,0x64,0x5c,0x2b,0x87,0xcf,
++		0xcc,0x3c,0x87,0xe6,0x48,0x14,0xaa,0x5b,0xc3,0x6c,0x46,0x14,0x4f,0x5c,0xf1,0x73, /* 2240 */
++		0x44,0xb2,0x21,0xaf,0xfa,0xfd,0x31,0x4b,0xbb,0xdb,0x0d,0xa5,0x13,0x6c,0x9e,0xc4,
++		0x0c,0x11,0x1b,0x5e,0xee,0x2f,0x96,0x2c,0x1a,0xd2,0x3e,0x01,0xbc,0x0b,0x8f,0xcc,
++		0x94,0x8a,0xe0,0x06,0x5a,0x97,0x60,0x6d,0x3a,0xad,0xb1,0x7a,0x62,0x35,0xd5,0x5e,
++		0xcc,0xa7,0x97,0x94,0xeb,0x41,0x2b,0x66,0x18,0xfd,0xc7,0x2f,0x0f,0x08,0xfe,0x75,
++		0x28,0xc3,0x8e,0xbc,0x33,0x83,0x87,0x66,0x4e,0x41,0x04,0xda,0x34,0x60,0x18,0x56, /* 2320 */
++		0xf4,0x48,0x71,0xf4,0xad,0x61,0x45,0x3e,0x61,0x3b,0x18,0x62,0x8b,0x21,0xbf,0x73,
++		0x8a,0xb0,0x5c,0xc3,0x79,0x6c,0x20,0x81,0x72,0x28,0x17,0x45,0x59,0x2b,0xe9,0xef,
++		0xeb,0x38,0x31,0x0c,0x8b,0x75,0x25,0xea,0x39,0xc3,0xd5,0x98,0x73,0xd7,0x87,0x3b,
++		0x02,0x8c,0x93,0x07,0x1b,0x44,0x71,0xab,0x37,0x5b,0xda,0x39,0xf7,0xfd,0xd4,0x6e,
++		0xf1,0xc7,0xae,0x2b,0x64,0xcd,0x91,0x18,0xf0,0xd8,0x9e,0x54,0x1d,0xa9,0x89,0x24, /* 2400 */
++		0xa3,0x0b,0xec,0x08,0x95,0xe0,0x65,0x82,0xf0,0xfb,0xc5,0xfd,0x7b,0x6b,0xa8,0x09,
++		0x43,0xa3,0x79,0x58,0xb5,0xec,0x28,0x25,0xc6,0xd6,0xee,0x48,0x59,0x89,0x3d,0xa1,
++		0x98,0x7a,0x8f,0xb6,0x82,0xc7,0x51,0xd4,0x0f,0xda,0xdc,0x79,0xaa,0xbd,0x96,0x61,
++		0x07,0x47,0x4f,0xf9,0xcd,0x38,0xd3,0xc3,0x35,0xe6,0x31,0x03,0xb5,0xb7,0x6c,0x75,
++		0x38,0x24,0xd4,0x04,0x83,0x07,0x71,0xaf,0x0f,0x9c,0x91,0xc3,0x15,0x7d,0x46,0x1e, /* 2480 */
++		0x6c,0xc4,0xd7,0x28,0x81,0xa4,0x33,0xc3,0x06,0x93,0xe7,0xfb,0xe8,0xd2,0x45,0xda,
++		0x3c,0xa2,0xc6,0xc2,0xa1,0x64,0x5b,0xfd,0x3b,0x56,0x7f,0x94,0xe8,0xfa,0x08,0xf1,
++		0x8b,0xbd,0x3e,0x17,0xb9,0xfb,0x1d,0x75,0x75,0x7d,0xe7,0x7a,0x74,0xc6,0x38,0xb3,
++		0xa4,0xdf,0x11,0x0c,0xf7,0x2a,0x7e,0x7e,0x22,0x4e,0xc4,0x91,0xf6,0xd1,0x80,0x29,
++		0xf3,0x07,0x01,0x28,0x07,0xb9,0x1f,0xed,0x29,0xc2,0x31,0xb0,0x48,0x5a,0x83,0xcc, /* 2560 */
++		0xd1,0xcf,0xf1,0x77,0x09,0xcc,0x48,0x0f,0x23,0x01,0x6c,0x62,0xc5,0x3b,0xbb,0x56,
++		0x92,0x8f,0x72,0x58,0x7f,0x50,0x2e,0xaf,0x8d,0xaa,0x84,0x8a,0x1b,0xf5,0xd6,0xfa,
++		0x49,0xf8,0x04,0x6b,0x36,0xad,0x78,0x0c,0xe4,0xd4,0x3e,0x99,0x9c,0x75,0x3f,0x73,
++		0xa7,0x7b,0x9d,0xd4,0x2c,0x08,0x54,0x3f,0xd2,0x92,0x02,0xf4,0xa7,0x7e,0x6d,0xce,
++		0x62,0xe3,0x5b,0x81,0x4c,0x96,0x71,0x24,0x70,0x72,0x0f,0x34,0xab,0x1a,0x42,0x7c, /* 2640 */
++		0x24,0xc2,0x85,0xe6,0xb6,0x42,0x63,0xae,0x97,0xa0,0x44,0x7e,0xf9,0xf5,0xc8,0x54,
++		0xa2,0xb8,0x50,0x96,0xea,0xca,0x9a,0x53,0x5d,0xd4,0x5b,0xb1,0x55,0xfd,0x64,0x71,
++		0x28,0xb6,0xae,0x83,0xb5,0x5a,0x5b,0x9e,0xc9,0x73,0xce,0xdc,0x49,0xa6,0x78,0xa3,
++		0x1c,0xb1,0x40,0xfa,0xbb,0x85,0xad,0xc9,0x9e,0xfb,0xab,0xbd,0x50,0x65,0xda,0xb4,
++		0x40,0x6b,0x65,0x4a,0xee,0x8f,0xe8,0x34,0x4f,0x68,0xe7,0xe3,0x4e,0xff,0xd0,0x72, /* 2720 */
++		0xa1,0xe9,0xc2,0x45,0x46,0x83,0x9c,0xa6,0x11,0xc4,0x56,0xbe,0x18,0x94,0x2c,0x17,
++		0xf2,0x8e,0xbf,0x7f,0x5b,0xdb,0xfd,0xe9,0x38,0x52,0xce,0xcc,0x45,0xfb,0xa6,0xf5,
++		0xb4,0x44,0x1c,0x1d,0xdb,0xc2,0x50,0x52,0x50,0xbf,0xb8,0xfc,0xaa,0x73,0xe8,0x87,
++		0x45,0x3a,0xde,0x8b,0x43,0x28,0xb7,0xe1,0xa8,0x38,0x4d,0x21,0x2b,0x3a,0x3a,0xbf,
++		0x4a,0x1e,0x61,0xa5,0x31,0x98,0x64,0xf3,0xa3,0x39,0x07,0x67,0xb5,0x29,0xc5,0xf3, /* 2800 */
++		0x27,0x01,0xdd,0xd4,0x91,0xca,0xae,0x66,0x23,0x04,0x99,0xe5,0x25,0x4f,0x6f,0x74,
++		0x84,0x4d,0x62,0x33,0xed,0x49,0x3c,0x8a,0x6c,0xf5,0x60,0xba,0x6b,0x84,0xdf,0x99,
++		0x4e,0xe2,0x34,0x1f,0x0a,0xeb,0x87,0xd0,0x22,0x77,0x0c,0x7b,0x7f,0x47,0x98,0x5c,
++		0xa8,0x05,0xc7,0xcb,0xad,0x10,0x6b,0xd8,0x7e,0x6e,0x8b,0xa5,0x71,0x21,0xa8,0x9b,
++		0x63,0xbb,0xdb,0x71,0x1d,0x56,0xfd,0xee,0xad,0xc3,0x81,0xf9,0xef,0x4c,0x5c,0xee, /* 2880 */
++		0x24,0x29,0x4e,0x02,0xc8,0x02,0x37,0xf2,0xc3,0x15,0xfd,0x00,0x58,0xab,0xb9,0x71,
++		0xf7,0xc1,0xe3,0x3d,0xb2,0x5d,0xbf,0xcf,0x8a,0xc0,0x80,0x83,0x66,0x8c,0x6b,0xc8,
++		0x3f,0x60,0x1d,0x2e,0x49,0x27,0x66,0xda,0xcc,0x1b,0x1c,0xd8,0x5e,0x0b,0x07,0x13,
++		0xe4,0x26,0xf6,0x4a,0xec,0x4c,0x3b,0xf2,0xe4,0xdf,0xbe,0x4d,0x49,0xb2,0xac,0x24,
++		0x50,0xc2,0x58,0x3a,0xe5,0x82,0x7c,0x65,0x4b,0x33,0xf8,0xf2,0xed,0xf8,0x88,0x93, /* 2960 */
++		0x2c,0x5f,0xe2,0x7c,0x1e,0xe4,0x2b,0xe5,0x93,0xc0,0x2f,0xaf,0x33,0xcb,0x31,0x60,
++		0xf5,0xb7,0xe3,0xd1,0xa2,0xc9,0x90,0x6e,0x2d,0x3c,0x5e,0x58,0x1d,0x23,0x53,0x86,
++		0x08,0x68,0xd3,0xcb,0x78,0x76,0xf9,0xf3,0x83,0xcf,0x32,0x37,0x98,0xd8,0xec,0x85,
++		0x3f,0x9a,0xe7,0x3c,0x3e,0x45,0x0a,0x3e,0x16,0x7d,0x94,0xa1,0xac,0x31,0xc1,0xbe,
++		0x1a,0x74,0xf8,0x45,0xec,0xc9,0xa6,0x5c,0x90,0x14,0x4c,0x00,0x60,0x48,0x59,0xd5, /* 3040 */
++		0x5e,0xf4,0x7a,0x17,0xf9,0x5b,0xd7,0x4f,0x29,0x46,0x56,0x52,0xa1,0x8c,0xb6,0x15,
++		0x95,0x19,0x1f,0xe8,0xd2,0x65,0xd8,0xf3,0xb9,0xb3,0xed,0x42,0x34,0xe0,0x74,0x1e,
++		0x34,0x22,0x90,0xe3,0xe3,0xa4,0x0d,0x74,0x52,0x98,0x19,0x4e,0x05,0xd0,0x34,0x4d,
++		0x5a,0x24,0xe6,0x2c,0x43,0x78,0x98,0xfb,0x55,0x68,0x4f,0x3d,0x51,0x93,0x47,0x09,
++		0x91,0x1d,0x80,0xbd,0x97,0xd4,0xfd,0x97,0x92,0x15,0xfa,0x31,0x24,0x3f,0xdd,0x1e, /* 3120 */
++		0x05,0x9f,0x79,0xff,0x4a,0x8f,0x70,0x23,0xff,0xcb,0x98,0xae,0x61,0xef,0x6f,0xfc,
++		0x33,0xde,0xee,0x30,0x35,0xb2,0x06,0xa4,0xb4,0x9e,0xf2,0x98,0x64,0x6e,0x62,0xfe,
++		0x9a,0x4e,0x23,0x69,0xd5,0xfb,0x56,0x7c,0xa1,0x16,0xd7,0x06,0xdc,0x9d,0x84,0x84,
++		0xf6,0xa8,0xd0,0x82,0x43,0xb2,0x98,0xf2,0xa1,0x7a,0xd5,0xf4,0xc0,0x45,0x49,0x19,
++		0x10,0x89,0x0f,0x0b,0x08,0x8f,0x1e,0xf0,0x29,0x63,0xe9,0xa6,0xe3,0x93,0x0a,0xff, /* 3200 */
++		0x92,0x9d,0x40,0xf6,0x91,0x05,0xb1,0x21,0xb7,0x78,0x68,0x6e,0x92,0x0f,0xc8,0x38,
++		0xd2,0x94,0x75,0xc6,0xda,0xc5,0xfe,0xed,0x51,0x58,0x62,0x5e,0x32,0x8a,0x50,0xd2,
++		0xe7,0x27,0x02,0x22,0x66,0xcb,0x0e,0x43,0xbb,0x1a,0xb8,0x9d,0x87,0x00,0xb3,0xf6,
++		0x22,0x3b,0x60,0x84,0xa7,0xc4,0xf7,0x06,0x19,0x4d,0x5a,0x5b,0x6b,0x8f,0xb2,0x0e,
++		0x68,0x85,0xab,0xc8,0xc9,0xd8,0xed,0xff,0x01,0x42,0x65,0x6e,0x8c,0x9e,0xa7,0xbf, /* 3280 */
++		0xeb,0xf2,0xf7,0x16,0xbc,0xcc,0x22,0x2d,0x3e,0x40,0x89,0xaf,0xe4,0x00,0x00,0x00,
++		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x0f,0x18,0x23,0x26,0x2d),               /* 3309 */
++	},
++	{ KEY_ML_DSA_87, 8, 75,
++	  chunk_from_chars(
++		0x30,0x34,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,
++		0x03,0x13,0x04,0x22,0x80,0x20),
++	  chunk_from_chars(
++		0x38,0x35,0x9f,0xbc,0xd7,0x95,0x82,0xcf,0xfe,0x60,0x9e,0x13,0x7e,0xe2,0xef,0xe8,
++		0xa8,0xdb,0xcb,0xad,0x18,0xba,0x92,0xbb,0x43,0x3a,0xb4,0xf0,0x9b,0x49,0x29,0x9d),/*   32 */
++	  chunk_from_chars(
++		0x30,0x82,0x0a,0x32,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,
++		0x13,0x03,0x82,0x0a,0x21,0x00),
++	  chunk_from_chars(
++		0x69,0x24,0xbb,0x42,0x57,0xa7,0xb9,0xaf,0xf0,0x95,0xc3,0x0b,0xb3,0x5c,0x6a,0xe4,
++		0x19,0x82,0x63,0x12,0x0f,0x80,0x39,0xaa,0x4e,0x78,0xe1,0x74,0xa7,0x86,0xce,0x00,
++		0x83,0x01,0xe6,0x66,0xf5,0x9d,0x3e,0xc5,0x04,0x4d,0xe4,0x56,0x78,0x8f,0xde,0x19,
++		0xeb,0x39,0x67,0x7b,0x5f,0x9f,0xe1,0x41,0x50,0xda,0x46,0x3a,0x70,0x6f,0x3b,0xaf,
++		0x71,0x5b,0x95,0x33,0x6b,0x2d,0x68,0x5a,0x7c,0xd7,0x88,0x07,0x13,0xe4,0x58,0x7b, /*   80 */
++		0xf7,0xd8,0x57,0xbf,0x7e,0x31,0x56,0x96,0xb8,0xd0,0xd9,0xd4,0x9e,0x14,0x29,0x18,
++		0xbf,0x09,0x74,0xe7,0xf4,0x32,0x37,0xd4,0xbe,0x3a,0xd3,0x94,0x59,0x9e,0x3d,0x39,
++		0xbb,0x76,0x49,0x93,0x25,0x53,0x44,0x7e,0x5d,0x5a,0xcc,0x34,0x99,0x93,0x01,0x76,
++		0xec,0xd3,0xa8,0x44,0xa4,0x25,0xf5,0x0d,0x05,0x11,0xc9,0x22,0x6c,0x4b,0x9a,0x24,
++		0xf2,0xa0,0x11,0xcd,0x88,0xd3,0x23,0x08,0xe0,0x31,0x2a,0x0c,0x87,0xcc,0x34,0xa9, /*  160 */
++		0x95,0x82,0x3c,0x65,0xf4,0xf0,0xf9,0x8e,0x50,0xc3,0x77,0x88,0xce,0x38,0xdc,0x28,
++		0xfb,0x8b,0x9b,0xfa,0xaf,0xa9,0x04,0xb5,0x41,0xee,0x71,0x2f,0x6a,0x04,0x1e,0x06,
++		0x11,0x37,0x4f,0x6b,0xf1,0x7e,0xac,0x0b,0xd5,0x6f,0x3b,0x6b,0xf3,0x36,0xda,0x92,
++		0x42,0x07,0x0c,0x24,0x69,0xa2,0x0c,0x4d,0x16,0x16,0x14,0x9a,0x61,0x59,0x25,0x20,
++		0x11,0xd2,0x99,0xf9,0x3f,0x98,0x6d,0x87,0x5d,0xd3,0x0b,0x38,0xa2,0x25,0x49,0x17, /*  240 */
++		0x45,0x70,0x13,0x8c,0x2b,0xb3,0xaa,0x9c,0xbe,0xa9,0x19,0x74,0xf3,0xd8,0x9b,0xf5,
++		0xae,0x32,0xbe,0x9e,0x58,0xb8,0x54,0xa2,0xf8,0xe8,0x6f,0xf7,0x67,0x80,0xc0,0x34,
++		0x90,0xf4,0x67,0xdb,0x06,0x51,0xc2,0x0b,0x1d,0xf6,0x0e,0xb9,0x7a,0x3c,0x99,0xd9,
++		0xbd,0x66,0x4b,0xe6,0xa5,0xe4,0xc8,0xa8,0xad,0x4c,0xc3,0x63,0x90,0xd7,0x00,0x4e,
++		0x4b,0xb4,0x21,0xda,0xed,0x65,0x4c,0x35,0x7d,0xa4,0xd6,0x84,0x98,0x93,0x3e,0xc7, /*  320 */
++		0x17,0x77,0xad,0x64,0xc2,0xae,0x01,0x3c,0x73,0xeb,0x45,0x7c,0x68,0xef,0x9a,0x74,
++		0x5a,0xde,0xeb,0x4f,0xdf,0xc8,0x79,0xe7,0x74,0xd0,0x3f,0xaf,0x6b,0x14,0xaa,0xb1,
++		0x07,0x52,0xe2,0x4b,0x52,0xd0,0xf2,0xd9,0x4d,0x54,0x0a,0x1e,0xbe,0x10,0xf5,0x97,
++		0xe5,0x14,0x44,0x2d,0x6c,0x13,0xc2,0xe2,0x49,0x8e,0x8a,0xf3,0x01,0x7c,0x52,0xdb,
++		0x23,0x3a,0x90,0x71,0x7d,0xf2,0x5b,0x4d,0x07,0x2b,0x7d,0x88,0xee,0x87,0x31,0xd1, /*  400 */
++		0x68,0x24,0xc9,0x5d,0x1f,0xb9,0x83,0xc4,0x49,0xde,0xb4,0x66,0x27,0x60,0x60,0xfe,
++		0xe4,0xc7,0xee,0x38,0x14,0x51,0xf2,0x32,0xc2,0x9c,0x7c,0x32,0x20,0x85,0x0c,0x61,
++		0xd1,0xc3,0xc0,0x0d,0xb1,0xcd,0x97,0x26,0xa0,0x2a,0x56,0x60,0x9f,0x3a,0x65,0xd3,
++		0xd1,0x64,0x60,0x45,0x88,0xcd,0x9b,0x43,0x14,0x12,0xf1,0xad,0xd9,0x14,0xc5,0xc2,
++		0xda,0xbb,0xc9,0x04,0x67,0xc0,0xc4,0xea,0x5f,0x76,0xe2,0x4a,0xa6,0x18,0x76,0x5f, /*  480 */
++		0x8b,0x06,0x36,0xd7,0xb0,0x65,0xe1,0xf4,0xe6,0xf6,0x22,0xea,0xe1,0x71,0x52,0x45,
++		0x8c,0x76,0x65,0x86,0x77,0x2d,0x36,0x3f,0xa9,0x92,0x14,0xf4,0x72,0xb0,0xdb,0x8a,
++		0x1e,0x49,0xd8,0x2d,0x02,0x78,0xf2,0x95,0x8b,0x0a,0xaa,0x15,0x86,0xdb,0x13,0x4b,
++		0xdf,0xd2,0x43,0x87,0x42,0x49,0x50,0x07,0xe2,0xfe,0x5b,0x60,0xe2,0x46,0x39,0x92,
++		0x26,0x94,0x7a,0x12,0xea,0x17,0x63,0x1c,0xaa,0x53,0x46,0x87,0xcb,0x75,0xc0,0x60, /*  560 */
++		0xb4,0x79,0x7e,0xab,0x82,0x77,0xcc,0x4f,0x8a,0x7a,0x20,0x38,0x76,0x06,0xef,0xe2,
++		0xdb,0xd3,0xe7,0x36,0x24,0x92,0x77,0xd9,0x0f,0xca,0xb9,0x92,0xa8,0xc9,0x9e,0x85,
++		0xab,0x03,0xeb,0x4c,0xac,0x5d,0x88,0x55,0x39,0x58,0x52,0x8a,0xf9,0x29,0x74,0x71,
++		0x81,0x35,0xf1,0xd0,0xc7,0x93,0xeb,0x00,0x0e,0xa0,0xae,0xc3,0xec,0x18,0x58,0xfd,
++		0xd1,0x86,0x88,0xd1,0xda,0x27,0x27,0x8d,0xeb,0xf2,0xca,0x81,0x10,0xba,0x4a,0x20, /*  640 */
++		0x4f,0x79,0x30,0xe1,0xc8,0xce,0xec,0xaf,0xb7,0x3f,0x75,0xdd,0xb3,0x4c,0x5c,0x55,
++		0x96,0x8a,0x79,0x33,0x05,0x84,0x26,0xb5,0x5d,0x03,0x9f,0x72,0x92,0xac,0x43,0xf6,
++		0x45,0x84,0xf6,0xdf,0x18,0x7a,0x1d,0x6b,0x00,0x3f,0x51,0x4c,0xc1,0x3b,0x26,0xc2,
++		0xf3,0x48,0x19,0x5a,0xa3,0x21,0xde,0x6a,0x27,0xec,0x11,0x34,0x8d,0xe5,0x0d,0x82,
++		0x5a,0x29,0x64,0xc6,0x31,0x99,0x2e,0x4b,0x0b,0x42,0x5b,0x1b,0xeb,0x4f,0x96,0x00, /*  720 */
++		0xe3,0xad,0xc4,0x43,0x1c,0xf2,0xe8,0x8b,0x42,0x23,0xd2,0xdb,0x66,0x3c,0x3c,0xe7,
++		0x0e,0xf8,0x5d,0xdd,0x56,0xa9,0xba,0xf1,0x38,0xa9,0xd7,0xed,0xd8,0x94,0x13,0x1c,
++		0x3a,0x8f,0x41,0xa0,0x4e,0xf9,0xf8,0x67,0x52,0xb7,0x21,0x81,0xfa,0xbb,0x37,0xc8,
++		0x6b,0x87,0x7e,0x61,0xd6,0x0e,0xed,0x95,0xee,0xff,0xab,0xe6,0x37,0x6e,0x14,0xac,
++		0xa8,0x17,0xc5,0xf4,0x19,0x61,0xaf,0x8a,0x78,0x49,0xba,0xc0,0x94,0x91,0x7b,0x2d, /*  800 */
++		0x13,0x22,0x76,0xb6,0xb3,0x48,0x6a,0xff,0x95,0x0d,0x23,0xd4,0xaa,0xdc,0x24,0xce,
++		0x98,0xa5,0x26,0x9e,0x1c,0x69,0x91,0x79,0x60,0xa3,0x1e,0xe0,0x9a,0x52,0x7c,0x35,
++		0x81,0x75,0xca,0xa0,0xcb,0x1b,0x01,0x8e,0x95,0x26,0xd9,0x35,0x34,0xea,0xdb,0xac,
++		0xb5,0x2b,0x27,0x3d,0x73,0x5e,0x22,0xdd,0x0d,0x5c,0x28,0xfa,0x3e,0x47,0xcf,0xe9,
++		0x0b,0x52,0x15,0xae,0x24,0xf1,0x46,0xc3,0x46,0x4b,0xfe,0xaf,0x01,0xd2,0x8d,0xaa, /*  880 */
++		0x55,0x3c,0x1e,0x94,0x42,0x8a,0x10,0x4a,0x9d,0x78,0xae,0xc7,0x62,0x59,0x1e,0x88,
++		0x79,0xf7,0x68,0x51,0xcf,0xb4,0x64,0x85,0x66,0x72,0x1b,0x0c,0xac,0x1f,0x14,0xfe,
++		0x16,0x14,0x9a,0x9d,0x82,0x10,0xcc,0x8f,0x2f,0x50,0xde,0xf7,0xb4,0x6c,0x84,0x3b,
++		0xe9,0x3b,0xd8,0xd5,0x56,0x02,0x49,0x33,0x50,0xab,0x56,0x0e,0xa5,0xba,0x17,0x71,
++		0x64,0x23,0xbe,0x0e,0xb8,0x36,0x0a,0xb1,0x09,0xd8,0xfb,0x18,0xbf,0xea,0x04,0x08, /*  960 */
++		0x47,0xb7,0x33,0x51,0x45,0xd4,0xf2,0x00,0xd1,0x9c,0xf6,0xfe,0x7b,0xac,0x91,0x7f,
++		0x42,0x6c,0x9b,0x3d,0x39,0xa9,0xca,0x43,0x29,0x81,0x8f,0x24,0x0e,0x7d,0xa3,0x82,
++		0x76,0x10,0x72,0xf4,0xa6,0x50,0x5e,0xa8,0xe7,0x6c,0x1e,0x44,0x6f,0xeb,0x66,0x25,
++		0xe3,0x8d,0xdb,0xcd,0x3c,0xda,0x81,0xe8,0x3b,0xf7,0x68,0xf3,0xe0,0x1d,0x9d,0x26,
++		0x3b,0x36,0x73,0x03,0xae,0x15,0x6c,0x0b,0x71,0x83,0x36,0x4a,0x1e,0x79,0x41,0xa0, /* 1040 */
++		0x92,0x98,0xa3,0xad,0xf7,0xbd,0x23,0x1e,0x61,0x14,0xb9,0xdc,0xe7,0x95,0x2b,0x11,
++		0x3f,0x78,0x16,0x31,0x38,0xb9,0x26,0x6f,0x84,0x3f,0x1e,0xd9,0x7d,0x9c,0x2b,0x16,
++		0x3a,0x6e,0x8b,0xd4,0xc1,0xab,0x4e,0x17,0x93,0x67,0xc5,0xac,0x96,0xce,0xcf,0x50,
++		0x50,0xfe,0x82,0x1f,0xdf,0xa4,0x4e,0x9e,0x68,0x0b,0x61,0xc6,0x01,0x89,0x32,0xdf,
++		0x71,0x78,0x11,0x45,0x9a,0xf2,0x54,0x2e,0x2c,0xde,0x77,0x17,0x8c,0x2e,0x98,0x80, /* 1120 */
++		0xf0,0x11,0xe4,0x05,0xea,0xfa,0x59,0xc8,0xcb,0xbe,0xd7,0x6e,0x5a,0x19,0x41,0x10,
++		0x4b,0x1b,0x9d,0x3a,0x60,0x49,0x1c,0x95,0x47,0x55,0xe0,0x2e,0x89,0x41,0x03,0xf1,
++		0xf4,0x97,0x74,0x75,0xe9,0xea,0x36,0x60,0x9f,0xd6,0x7c,0x9d,0xe3,0x18,0xed,0xa2,
++		0x37,0x0d,0xcc,0xdb,0xb9,0xce,0xf7,0xae,0x63,0x60,0x90,0x5e,0xc2,0x20,0x83,0x8c,
++		0x97,0x69,0x82,0x34,0x41,0xcd,0xd0,0xda,0x8e,0xf0,0xab,0xe5,0xf2,0xd1,0xd7,0x6e, /* 1200 */
++		0x2f,0xe0,0x8f,0xef,0x53,0xde,0x1d,0x61,0x66,0xab,0x1a,0x92,0xb1,0xac,0x09,0x3e,
++		0x5a,0xbf,0x76,0x58,0xc4,0xb5,0x72,0x87,0xf2,0xd1,0xfd,0x7b,0x82,0xde,0xda,0xf8,
++		0xd5,0xa4,0xfb,0xac,0x4b,0x35,0xd5,0x82,0x31,0x69,0x4e,0x16,0x24,0x97,0x57,0x8a,
++		0xbd,0x7a,0xa7,0xc8,0xfe,0x7b,0x35,0x41,0xa7,0xf1,0x8e,0x54,0xe8,0xb7,0xf0,0x87,
++		0x64,0xc5,0xe6,0x84,0x49,0xdf,0x65,0x59,0x01,0x54,0x98,0x32,0xd6,0x28,0xfa,0x63, /* 1280 */
++		0xd2,0xb2,0xc5,0xa1,0x50,0x93,0x39,0x94,0xa9,0x86,0x33,0x17,0xad,0x40,0xd7,0x78,
++		0xd9,0xd2,0xc0,0x5c,0x78,0x98,0x85,0x0b,0x90,0x17,0x32,0x23,0xc7,0xa0,0xaf,0x89,
++		0x0f,0xd7,0xe6,0x62,0x21,0xb6,0xf0,0x63,0x18,0xb2,0xed,0x5e,0x19,0x9c,0xb4,0x24,
++		0x88,0x5a,0xb8,0x41,0xe7,0xa4,0x72,0x6f,0xab,0xa2,0xf9,0xbb,0x53,0xbc,0x32,0x36,
++		0x43,0x4c,0x35,0xfb,0xbe,0x4b,0x1a,0x0f,0x93,0xf5,0x0c,0x37,0x89,0x6c,0x29,0xf8, /* 1360 */
++		0xe3,0x02,0xad,0x31,0xed,0x33,0x31,0xd6,0x20,0xe3,0xb6,0x29,0x45,0x51,0x01,0xa1,
++		0xf1,0xcc,0x7b,0xa5,0xe4,0x6e,0x68,0xed,0x4a,0x8c,0xcc,0x87,0xb4,0xdc,0x75,0xbc,
++		0x01,0x62,0xb6,0x33,0x0f,0x83,0x3f,0xba,0x25,0x75,0xdf,0xaf,0x5b,0x5f,0x28,0xbc,
++		0x54,0xff,0x2b,0xa8,0x1e,0x7a,0x47,0x31,0x3c,0x15,0x48,0x2b,0x60,0x5e,0x66,0xbb,
++		0x38,0xc6,0x19,0x8f,0x13,0x92,0x10,0x40,0x80,0xfb,0xe7,0x8b,0x86,0xb1,0xbc,0x9a, /* 1440 */
++		0x6f,0xb8,0x81,0xf5,0xc7,0x82,0x01,0x47,0xe6,0xba,0x14,0xb8,0x1a,0xcc,0xf2,0x0c,
++		0xae,0x96,0x64,0x10,0x94,0xc2,0x16,0x90,0x2e,0xa5,0xc1,0x25,0xf6,0xc9,0x35,0xa1,
++		0x50,0xd7,0xc9,0xac,0xc5,0xd9,0xe2,0xe5,0xd9,0x0e,0x38,0xc0,0x50,0x3a,0xa9,0x42,
++		0x60,0x17,0xc7,0x6a,0xaf,0xcd,0x52,0x61,0xb5,0x06,0x27,0x4e,0xc1,0x3a,0x96,0x79,
++		0xfb,0x09,0x79,0x60,0x27,0xa4,0xbb,0x75,0x9d,0x92,0x82,0x79,0xb9,0x4d,0x84,0x1a, /* 1520 */
++		0x09,0x73,0x93,0xbf,0x7e,0x5b,0xd6,0x9a,0x49,0x6c,0xc3,0xde,0xcd,0x2b,0x0f,0x07,
++		0xf8,0x33,0x92,0xaa,0xde,0x33,0xdc,0x51,0xb2,0xa8,0x4f,0x6a,0x07,0x63,0x5d,0xc0,
++		0xef,0x57,0xa9,0xad,0x59,0x59,0xb6,0xa5,0x0b,0x7b,0xa5,0x09,0xad,0x5b,0x11,0xfa,
++		0xd2,0x6b,0x41,0x9f,0x9f,0x1e,0x3f,0x9c,0x73,0x29,0xb5,0xa9,0x53,0xd7,0xcc,0x87,
++		0xb2,0xde,0x21,0x06,0x11,0xcf,0x52,0xa6,0x39,0xef,0x2b,0x39,0x08,0x01,0x2c,0xb8, /* 1600 */
++		0x8e,0x1d,0x6f,0x57,0x62,0x50,0x79,0xcb,0x10,0x3d,0x6c,0x98,0x10,0x1a,0x11,0xbd,
++		0x22,0x33,0xb6,0x56,0x02,0xca,0x30,0x49,0xbd,0x32,0x05,0x20,0x41,0x9f,0x76,0xb0,
++		0x61,0xe3,0x59,0x8d,0xe3,0x81,0x52,0xc8,0x87,0x67,0xd1,0xa2,0x4f,0xbd,0x02,0xbb,
++		0x10,0xc3,0x8e,0xac,0xae,0x31,0x7d,0xe6,0xbb,0x28,0x7b,0x4d,0x2c,0xae,0x5d,0xa0,
++		0x21,0x49,0x65,0xd8,0x77,0x37,0x78,0x62,0x6e,0x9b,0x97,0x28,0x59,0xd8,0x48,0x2b, /* 1680 */
++		0x8d,0x05,0x47,0xe4,0xf5,0x6d,0xff,0x87,0x68,0x1d,0x5b,0xc5,0x12,0x0f,0x61,0x3f,
++		0xbb,0xd9,0x1e,0x1f,0x14,0xe6,0xde,0xfe,0x67,0x2e,0x2a,0x7e,0xab,0xcb,0xbb,0x9b,
++		0x11,0x08,0x2c,0x5e,0x70,0x0a,0xa0,0xb1,0xf7,0xc1,0x78,0x5f,0xce,0xd1,0x9a,0x93,
++		0xaf,0xe7,0xc5,0x9f,0xa2,0x51,0x9b,0xcd,0xeb,0x49,0x4c,0x3d,0x13,0xb2,0x12,0x5f,
++		0x38,0x53,0x23,0xb8,0x16,0xc6,0x8f,0x8f,0x56,0x28,0xc7,0xc2,0xab,0xfd,0x02,0x78, /* 1760 */
++		0xa3,0x37,0x07,0x3d,0xa7,0x4d,0x16,0x09,0x96,0x98,0xc4,0xb1,0x14,0xe8,0xa8,0xce,
++		0x34,0x4e,0x0a,0x15,0xd0,0xfc,0x7e,0xd4,0x97,0xb0,0x01,0xd5,0x3d,0x4c,0x96,0xdc,
++		0x39,0x54,0xd3,0xb4,0xb9,0x56,0xcb,0x9d,0x2a,0x27,0x2c,0x51,0xf1,0x55,0x9b,0x22,
++		0x90,0x4b,0x40,0xcc,0x85,0x31,0xe4,0x0c,0xc4,0x12,0xc6,0x8c,0xb6,0xee,0xa4,0xa4,
++		0x09,0x0b,0x38,0xe2,0x79,0x73,0x29,0x98,0x54,0x67,0xe8,0x18,0xa5,0x24,0xd3,0x22, /* 1840 */
++		0x8e,0xac,0xae,0x78,0x25,0xd3,0xda,0xd2,0xea,0xa4,0x22,0xfd,0xc7,0x7a,0xed,0x71,
++		0xa2,0x05,0xda,0x78,0x38,0xd9,0x45,0xe7,0xfe,0xc3,0x7e,0x4d,0xca,0x67,0xe5,0x04,
++		0xce,0x35,0xe5,0xb0,0x45,0xf5,0x6f,0x1e,0x8d,0x75,0x29,0xeb,0xd6,0xf1,0xaf,0x7b,
++		0x6e,0x93,0x9e,0x2b,0x7a,0xb4,0x02,0x7d,0x37,0xa5,0x13,0x5d,0x17,0x2d,0xa1,0xaf,
++		0x9c,0xa2,0xf7,0x28,0xa6,0xf3,0x7d,0xe6,0x0d,0xd2,0x3d,0x97,0xd1,0x1e,0x75,0xab, /* 1920 */
++		0x1f,0xd5,0x1f,0x8e,0x9a,0x13,0x97,0xe5,0x82,0x21,0x59,0xdb,0x58,0x38,0x02,0xb3,
++		0x2e,0xeb,0xb4,0x56,0x7e,0xce,0x37,0x46,0xd1,0xae,0x33,0x31,0x47,0x85,0x64,0x3d,
++		0xd2,0xa0,0x74,0x1e,0x7f,0x1b,0xf2,0xd2,0x61,0xf2,0x21,0x24,0xe8,0xdd,0xd0,0x8c,
++		0x64,0x0a,0x48,0xb5,0x47,0x17,0x51,0x7c,0x21,0xcd,0x32,0x53,0x28,0xbc,0x23,0x9c,
++		0xa0,0x28,0xb2,0x63,0x0d,0x06,0x3c,0x8c,0xc2,0x0b,0xe9,0xbd,0xb4,0x85,0x02,0xda, /* 2000 */
++		0xdd,0xe7,0x3f,0xfe,0xd5,0x96,0x38,0x16,0x53,0x3e,0x02,0x0a,0xed,0x12,0x08,0x53,
++		0x62,0x55,0xb1,0xcc,0xe9,0x85,0x43,0x31,0x27,0xff,0x4f,0x04,0xd5,0xb1,0xe2,0xf2,
++		0x10,0x87,0x04,0xb8,0xb9,0x66,0x58,0x8c,0x01,0x56,0xaf,0xc2,0xae,0x19,0x29,0x86,
++		0xfb,0xec,0x44,0x3b,0xae,0xf6,0xcb,0x85,0xa6,0xf2,0x9c,0x77,0x92,0x40,0x5a,0x24,
++		0x11,0x47,0x10,0xae,0x1c,0x74,0x64,0x44,0xfd,0xf5,0xfb,0x65,0x9e,0x5e,0x34,0x68, /* 2080 */
++		0x26,0x20,0x7b,0x8c,0x54,0x46,0x3a,0x06,0x17,0xce,0x17,0xff,0x33,0xe4,0x0f,0x93,
++		0x1f,0xe5,0x76,0x71,0x5c,0x93,0x2e,0xf2,0x9f,0xd7,0x6b,0x04,0xa6,0x9b,0x58,0xe0,
++		0x30,0x3d,0x8e,0xf2,0x56,0x78,0xc8,0xb7,0x0a,0xf1,0x2e,0x90,0x45,0x59,0x1c,0x04,
++		0xe8,0xb7,0x71,0x06,0x94,0x04,0x15,0x17,0x7e,0x86,0x85,0x93,0xa0,0x9c,0x7e,0x14,
++		0x61,0x9a,0x4b,0x33,0x2f,0x9a,0xdc,0x3a,0x65,0x8b,0x86,0x01,0x7f,0x32,0x65,0x6c, /* 2160 */
++		0x54,0x29,0xc1,0x15,0xe1,0x10,0x03,0x7a,0x8c,0xc7,0xe5,0x44,0x67,0x7d,0x2d,0xd2,
++		0x39,0xa5,0x9d,0x54,0xd0,0xf3,0xc7,0x46,0x0e,0xc1,0x52,0x08,0x34,0x6b,0xa5,0x6d,
++		0xf5,0x08,0x6c,0x5d,0xbc,0xc4,0x1e,0x0c,0x95,0xfc,0xb6,0x86,0x1c,0x2c,0x0c,0x32,
++		0xaa,0xf3,0x45,0x4e,0xfe,0xe2,0xff,0xba,0x21,0x4b,0x43,0x0e,0xf2,0x48,0xa5,0x9b,
++		0x32,0x44,0x4d,0x8d,0x0d,0x3d,0xb8,0x7c,0x9d,0x4b,0x15,0x36,0xd1,0x57,0x72,0x8e, /* 2240 */
++		0xe7,0x58,0x5e,0xf5,0x32,0x77,0x6a,0x00,0x3a,0x02,0x3c,0x0a,0xb0,0xe9,0xff,0x55,
++		0x71,0x08,0xc3,0x90,0x68,0x4d,0x56,0x5a,0x66,0x50,0x63,0x26,0x6a,0xe6,0x67,0x0e,
++		0xd5,0x3b,0x0f,0xaf,0x8f,0xf6,0x78,0x29,0xbb,0x73,0x78,0x25,0xb1,0x53,0xa9,0x33,
++		0x8c,0xbe,0x3d,0xf1,0xa4,0x62,0x84,0x9b,0x93,0xa8,0x1f,0x84,0xed,0x07,0xbe,0x6d,
++		0x62,0x40,0x00,0x32,0x74,0x73,0x7f,0x61,0x8d,0xcb,0x26,0xe4,0x82,0x52,0xce,0x42, /* 2320 */
++		0x04,0xdd,0x31,0x39,0xff,0x68,0x76,0xf4,0x3b,0x30,0x5d,0x83,0x56,0x20,0xfe,0xdf,
++		0x79,0xaa,0x67,0x43,0x3d,0xc2,0x52,0x87,0x32,0x0e,0x99,0x17,0x96,0x7b,0x70,0xb2,
++		0xd8,0x66,0xd1,0x7b,0x69,0x8b,0xff,0xf2,0xb3,0xab,0x95,0x14,0x94,0x9e,0x58,0xb5,
++		0x7c,0x68,0xa4,0x54,0x12,0xc1,0xfc,0x42,0x1c,0x76,0x8b,0xf5,0xee,0x8a,0x10,0xc8,
++		0xae,0xf5,0x69,0x26,0xf5,0x1e,0xc6,0x2c,0x11,0x56,0x9f,0x31,0xaa,0x51,0x78,0x68, /* 2400 */
++		0xe5,0xca,0xd8,0x9e,0x95,0x80,0x66,0xeb,0x9e,0xdd,0x72,0x71,0xb3,0x1c,0xb4,0xb1,
++		0xd6,0xce,0x21,0x12,0x25,0xae,0xb5,0xb5,0x7f,0x74,0x97,0x19,0xda,0x07,0xec,0xbe,
++		0xfe,0x03,0x88,0x1d,0xde,0x3d,0x81,0xe4,0x13,0x5f,0x2d,0xc8,0x1a,0xf7,0x79,0x77,
++		0x6c,0x1b,0x80,0x57,0x16,0x2a,0x6c,0x98,0x2f,0xbb,0x4d,0xa6,0xa9,0xad,0x28,0x4a,
++		0xb1,0x0c,0x70,0x02,0x20,0x44,0xf4,0x6d,0x40,0x0b,0xf6,0xad,0x71,0x82,0xd1,0x97, /* 2480 */
++		0x78,0x99,0x83,0xbe,0x99,0x22,0x79,0x79,0xa1,0x33,0x4b,0xa1,0x49,0xd8,0x69,0xba,
++		0x1c,0x40,0x88,0x12,0x34,0x35,0xbf,0x97,0x85,0x41,0x35,0x6d,0xaf,0x17,0x1f,0x33,
++		0xad,0xb1,0xc9,0x79,0x07,0xa0,0xfb,0x58,0x45,0x07,0x4a,0x85,0xd2,0x6f,0x54,0x61,
++		0x35,0xae,0xd0,0xf9,0x1b,0xe4,0x53,0x9c,0x12,0xbf,0x94,0x11,0xe4,0xb5,0x56,0xf6,
++		0x87,0xd0,0x69,0xdb,0x6b,0x21,0xfe,0x2b,0x7f,0x32,0x18,0x87,0x44,0x8c,0xea,0x55, /* 2560 */
++		0xdb,0x19,0xfb,0xb8,0xb0,0x48,0x2a,0x55,0xae,0xc1,0x67,0x38,0xd7,0x4c,0xd2,0x65,
++		0x09,0x38,0x36,0xbe,0x99,0xd4,0xfb,0x53,0xe9,0xb0,0x14,0xb0,0x37,0xcd,0xbf,0xe9),/* 2592 */
++	  chunk_from_chars(
++		0x19,0xe4,0x22,0x2d,0xc6,0xaa,0x5c,0x9d,0xd6,0x18,0x33,0x31,0xb7,0x1d,0x09,0x01,
++		0x05,0xff,0x19,0x0c,0x8c,0xb6,0x72,0x48,0xfa,0xe7,0xf7,0x44,0xf8,0xb2,0x6f,0xcd,
++		0xed,0x9d,0x03,0x9b,0x11,0xe4,0x5b,0x07,0xe2,0xfd,0xc2,0xe5,0x09,0xcf,0xa9,0x28,
++		0x91,0x2e,0xe9,0x0d,0x8b,0x78,0xb3,0x77,0xf6,0x54,0x42,0xe6,0xa1,0x44,0x25,0xdb,
++		0xea,0xaa,0x98,0x7d,0xd4,0x1c,0x2b,0x61,0x19,0xef,0x63,0x4b,0x70,0x60,0x77,0x4b, /*   80 */
++		0x76,0x7b,0xe3,0x72,0xa9,0xb4,0x1e,0xb5,0xd5,0xe5,0xbf,0x9e,0x5d,0x9c,0xd0,0x63,
++		0x84,0x4b,0xea,0x7c,0xe4,0xee,0xf1,0x3c,0x1d,0xe8,0x2a,0x3f,0x10,0x45,0xe5,0xc0,
++		0x73,0xff,0xb5,0x0c,0xdd,0x86,0x5b,0xac,0xc3,0x27,0x9e,0xc9,0x54,0x9c,0x30,0x29,
++		0x7e,0xf4,0xb7,0xb0,0xae,0x06,0x59,0x39,0x7f,0x58,0x86,0xad,0x73,0x7d,0x83,0x16,
++		0x75,0x86,0x25,0xd9,0xc4,0x62,0x6b,0xf3,0x27,0xca,0x15,0xcc,0x5d,0x31,0x36,0x0c, /*  160 */
++		0x51,0x0b,0x2f,0x48,0x06,0xd6,0x75,0xce,0xe2,0x34,0xc0,0xb2,0x3f,0x50,0xc1,0xbc,
++		0xfa,0xb5,0x97,0x4b,0xa1,0xf0,0xcc,0xac,0xa4,0x7d,0x0c,0x1c,0xc8,0x99,0x2f,0xe9,
++		0xce,0xcc,0xad,0x52,0x3e,0x5b,0xa7,0x0b,0x26,0x8b,0x49,0x4e,0xa3,0xff,0xfc,0xdc,
++		0x14,0xe6,0x4d,0x14,0x50,0x69,0x01,0x30,0x49,0x75,0x6d,0xfb,0x75,0x54,0x12,0x8b,
++		0x66,0xd1,0x94,0xd5,0x01,0x72,0x31,0x4f,0x56,0x6b,0x73,0x08,0x75,0xf6,0xfc,0xb4, /*  240 */
++		0x06,0x08,0xeb,0x9e,0x15,0x0b,0x08,0xff,0x2e,0x80,0x85,0x91,0x1c,0x7a,0xbb,0x58,
++		0x49,0xb0,0xa6,0xf3,0x50,0x7b,0xfe,0xc3,0x50,0x44,0x1b,0xcc,0xdf,0x7b,0x0e,0x9f,
++		0x41,0x14,0xba,0x64,0x18,0x85,0xfa,0xa2,0x9b,0xfa,0xe3,0x7f,0x7d,0xa4,0x4f,0xff,
++		0xa9,0xdf,0x02,0x1a,0xe2,0x91,0x55,0x4a,0xd1,0x76,0x2b,0x97,0x72,0xe7,0xf2,0xd5,
++		0x92,0x1b,0x20,0x37,0x59,0x41,0x02,0x0d,0xdb,0x43,0xc2,0x17,0xbf,0x0f,0x1d,0xe2, /*  320 */
++		0xec,0x64,0x09,0x17,0xb9,0x7a,0xb0,0xb4,0xff,0x8e,0xb8,0x6d,0x79,0x0b,0x17,0x98,
++		0xf7,0xc8,0x77,0xd6,0x18,0x6d,0xd4,0xb3,0x96,0xee,0x2c,0xa7,0x59,0x34,0xa8,0x03,
++		0xf3,0x21,0xe2,0xc9,0x22,0xe8,0xdd,0x75,0x1e,0x94,0x65,0x3a,0xe8,0xe6,0x5f,0x3e,
++		0xe9,0xd8,0xff,0xa4,0x10,0xa7,0x2a,0xb5,0x34,0xfc,0xe4,0xc5,0xb4,0x78,0xaa,0x3c,
++		0x7a,0x7a,0xfc,0xff,0x89,0x4b,0x1d,0x06,0x1b,0x06,0x30,0xf4,0x04,0xf4,0x4b,0xc5, /*  400 */
++		0x5d,0x11,0x79,0x25,0xf0,0x94,0x50,0x40,0xd2,0x79,0x2b,0xa4,0x30,0x16,0xc9,0x4a,
++		0xb5,0x05,0x9e,0xcc,0xd5,0xb3,0xbf,0x66,0xb4,0x0b,0x62,0xe8,0x7b,0xfd,0xdb,0x85,
++		0xf7,0x7b,0x0c,0x85,0xe1,0xbb,0x43,0x48,0x60,0x89,0xef,0x63,0x29,0xfa,0xfd,0xab,
++		0x32,0xaf,0x63,0x68,0x92,0x3e,0x6e,0x23,0xfe,0xd5,0x74,0x86,0x68,0x79,0x27,0x38,
++		0x84,0x60,0x26,0x74,0x9a,0x9c,0xf7,0x6a,0x8b,0x18,0xba,0x3d,0xbf,0x8b,0xf1,0xf0, /*  480 */
++		0x3a,0x3b,0x80,0x51,0x93,0x1d,0x92,0x38,0x7b,0x09,0x72,0x17,0xf2,0x3a,0x15,0x51,
++		0x93,0x8d,0xf4,0x03,0x7c,0x55,0xcf,0xc4,0xe6,0x9a,0xf7,0xcf,0x15,0x29,0x65,0x68,
++		0xf1,0x98,0xf3,0xc9,0x2c,0x72,0xd4,0x99,0x69,0x28,0x41,0xaa,0xb1,0x75,0x4f,0x8c,
++		0x1b,0xe6,0x2f,0xd3,0x5a,0x87,0x02,0x4d,0xde,0x48,0x8c,0xac,0xcb,0x44,0x72,0xbc,
++		0x1a,0x2c,0x54,0x3b,0x91,0x07,0x41,0x62,0xbf,0x5c,0xe1,0x43,0x48,0x42,0xb4,0xea, /*  560 */
++		0xea,0x9f,0x91,0x26,0x2e,0xd7,0x71,0x02,0xa1,0xd2,0xcd,0x9b,0xf5,0x91,0xc4,0x99,
++		0xf3,0x8f,0x4d,0xb8,0xf0,0xbd,0xe4,0xa0,0xc0,0x54,0x43,0x5e,0x5b,0xd0,0x44,0xad,
++		0x7c,0x7b,0x70,0x77,0xbb,0xa3,0x19,0xb4,0x87,0x39,0xe2,0x17,0x16,0x3f,0xa8,0xd2,
++		0x8a,0xea,0x52,0xe2,0x43,0xdd,0xb7,0x99,0x13,0x65,0x92,0x4d,0x78,0x1a,0x23,0x93,
++		0x4d,0xb4,0x02,0x4a,0x38,0x0a,0xa2,0xb7,0x7e,0xc5,0x3a,0x42,0xc3,0x31,0x48,0xc0, /*  640 */
++		0xe1,0x0f,0x35,0x6f,0xc6,0x9d,0x8c,0x56,0x59,0xa6,0xf5,0x8f,0x79,0x0f,0xcc,0x96,
++		0x40,0xb5,0xb1,0x7f,0x32,0x26,0x50,0x7d,0x81,0xdf,0x56,0xf5,0x3e,0xb5,0xf0,0x9e,
++		0x81,0x19,0xf5,0xf3,0xf5,0x1a,0x6f,0x8c,0xcf,0x97,0x33,0x8b,0x35,0xcd,0xf6,0x94,
++		0xaf,0xb8,0xdb,0x8d,0xa0,0xfc,0x06,0xc0,0xf6,0x93,0x1d,0x5f,0x36,0xa3,0x79,0x15,
++		0x1d,0x1d,0x37,0x9d,0xc3,0x36,0x75,0x5c,0x83,0xf5,0x3d,0xa3,0x6a,0xde,0xd7,0x69, /*  720 */
++		0x9d,0xea,0xcb,0xb9,0xe5,0xb6,0xa7,0x61,0x93,0x8a,0x2a,0x7d,0x0d,0x04,0xe4,0x9d,
++		0x26,0x7e,0x5c,0x25,0xa5,0xce,0xdb,0x91,0x01,0x14,0x65,0x1d,0x49,0x68,0x82,0x3b,
++		0x43,0x80,0x01,0x53,0x7b,0xca,0xbc,0xe2,0x85,0x4e,0x90,0xa4,0x6c,0x89,0xc4,0x2c,
++		0x33,0xfb,0x14,0x0f,0x2a,0xcc,0x9e,0xe1,0x44,0x57,0x18,0x68,0x81,0xf4,0x41,0x79,
++		0x8d,0x22,0x10,0xda,0x1c,0xf6,0xe7,0xa5,0xfd,0x4f,0xd2,0x7f,0xdb,0x3b,0x11,0x86, /*  800 */
++		0xd9,0xdc,0x74,0x3c,0x88,0x87,0xf3,0xdb,0x0d,0x8e,0xee,0x44,0x7c,0xb5,0xcb,0x7f,
++		0x5f,0x35,0xe2,0xff,0x71,0x8a,0x19,0xcb,0x20,0x33,0x83,0x0f,0xac,0xe8,0xd6,0xd3,
++		0xdf,0xdd,0xb9,0x86,0x2d,0x74,0x95,0xa6,0xdd,0x6e,0x32,0x99,0x16,0x16,0xa4,0xaa,
++		0x62,0x6d,0xff,0x21,0xae,0xa0,0xfd,0xe8,0x50,0xeb,0xff,0x8a,0x7a,0xa5,0x9b,0x23,
++		0xbb,0x72,0xcc,0x62,0x40,0x52,0x79,0x42,0x7b,0x9a,0x67,0x5e,0xb1,0xd4,0x2a,0x83, /*  880 */
++		0x2c,0x1b,0xb9,0xa7,0x66,0x30,0x48,0x73,0xbc,0xda,0xb6,0x42,0x5f,0xaf,0xaa,0xf3,
++		0xa4,0x07,0x68,0x91,0xa3,0xe5,0x4a,0x0b,0x14,0x27,0x3a,0xd5,0xc3,0xcd,0x8d,0x28,
++		0x03,0xf3,0x68,0xd4,0x31,0x60,0xbb,0xcd,0x0f,0x6a,0x77,0x41,0xd9,0xfb,0xfa,0x88,
++		0x6d,0x93,0x8d,0xee,0x71,0x2e,0x97,0x67,0xab,0xa4,0xc4,0x30,0x09,0x68,0x0e,0x88,
++		0x2c,0x3e,0x88,0x8b,0xb6,0xc7,0x92,0x30,0xfc,0xbe,0x6c,0x7f,0x2a,0x3b,0x77,0xdd, /*  960 */
++		0x54,0x6b,0x73,0x41,0x9a,0x1c,0xd9,0x97,0xa1,0xd7,0x31,0x4a,0xe3,0xee,0x9d,0x9b,
++		0xee,0xb1,0x55,0x53,0x13,0xa1,0xe9,0x3b,0xa4,0x08,0xe9,0x44,0xff,0x21,0xd4,0x99,
++		0xc6,0x42,0x75,0xf4,0x4f,0xbe,0xb7,0x52,0x90,0x4d,0x9d,0xbd,0x84,0xa9,0xb3,0xed,
++		0x4f,0x2f,0xe1,0x84,0xbc,0x1a,0x11,0x7a,0x57,0x85,0x53,0x7e,0xa1,0x3d,0x95,0x11,
++		0x0a,0x7c,0xd4,0x93,0xc2,0xea,0x2f,0x01,0x56,0x67,0x5e,0x66,0x05,0xd4,0x2b,0x78, /* 1040 */
++		0x53,0x25,0x5d,0x1b,0x60,0x0a,0x76,0xd1,0xa4,0xfa,0xb4,0x26,0xf6,0x2d,0x81,0xbd,
++		0x1f,0xaa,0x9e,0x04,0xd2,0x06,0x90,0xcc,0x84,0xb3,0x38,0x80,0xdf,0x66,0xfb,0x20,
++		0x0d,0xdd,0xd6,0x07,0x56,0x87,0x4b,0x79,0x5b,0xaa,0x38,0x6a,0xb9,0x79,0x6e,0x90,
++		0x11,0x06,0x36,0xf0,0xe1,0xf4,0x8a,0x46,0xe0,0xce,0xcb,0xae,0x49,0x96,0xc1,0xad,
++		0x88,0xb2,0xaa,0x1a,0x90,0x7d,0x04,0xf4,0x35,0x12,0x37,0xc2,0xcd,0x4d,0xfc,0xf5, /* 1120 */
++		0x35,0x58,0x6c,0xb8,0x18,0xe4,0xda,0x02,0xdf,0x77,0x36,0x75,0x0a,0xcb,0x48,0xdd,
++		0xef,0xe0,0x7a,0x59,0x1b,0xcc,0xc3,0x6c,0x6e,0x1b,0xaf,0x98,0xf4,0x3a,0x24,0x86,
++		0x88,0xf4,0xa8,0xfa,0x86,0x3c,0x74,0x5b,0xae,0xa6,0xf1,0xd2,0xd3,0x6a,0xf7,0x49,
++		0x6c,0x28,0xf0,0x50,0x0c,0x18,0x30,0xa3,0xbf,0x90,0xf1,0x95,0x0d,0xab,0xaa,0xa6,
++		0x4d,0x4e,0xbd,0xba,0xd5,0xda,0x30,0xa6,0x37,0xe4,0x38,0x83,0x35,0x6e,0xe0,0x08, /* 1200 */
++		0xa6,0xf1,0x05,0x1c,0x2f,0xe8,0xc4,0x2d,0x67,0xfd,0x0d,0x4a,0x67,0xe0,0xa5,0x01,
++		0x22,0xcb,0x5f,0x2c,0x65,0x59,0xea,0x83,0xe6,0x10,0x44,0x16,0x9d,0xa8,0x57,0xff,
++		0x72,0x35,0x64,0x56,0xd1,0xc3,0x3c,0x2c,0x36,0xe1,0xe8,0xc2,0xd3,0x69,0xf2,0x70,
++		0xcd,0x97,0xef,0x16,0xff,0x58,0x67,0x21,0x29,0xfe,0xd3,0x41,0xc1,0xc0,0x6a,0x5f,
++		0x98,0xe3,0xce,0xac,0x83,0x21,0x9c,0xb2,0xac,0x0c,0x67,0x70,0xc0,0x55,0xc3,0x33, /* 1280 */
++		0xb0,0x3d,0x94,0x8b,0xc4,0x25,0x55,0xce,0xc3,0x35,0xd2,0x7b,0x14,0x3a,0x10,0x82,
++		0xcd,0xb4,0xeb,0x20,0xab,0xb6,0x1b,0x5f,0xf9,0x6e,0xfd,0x5b,0xd4,0xf7,0xf0,0xb3,
++		0xe3,0xce,0x76,0x43,0xe5,0x24,0x5a,0x9a,0xd3,0xcd,0xe9,0xfe,0x3a,0xfe,0xa9,0xe2,
++		0x88,0x69,0x10,0x71,0x1e,0xa5,0x64,0x62,0x50,0x23,0x45,0x18,0x53,0xd8,0xfe,0xf6,
++		0xba,0x23,0x14,0x19,0xbb,0x68,0x30,0x3e,0x23,0x8f,0x23,0x15,0xed,0xee,0x13,0x26, /* 1360 */
++		0x22,0x67,0x43,0x1b,0xdb,0x1d,0x4e,0xa5,0x13,0x8d,0x1c,0xd5,0xa9,0x54,0x45,0x9e,
++		0x03,0xba,0x94,0x79,0x73,0x09,0x8e,0x1d,0x7b,0xac,0xe4,0x3e,0xd8,0xff,0x65,0x5d,
++		0x28,0xf4,0xbe,0x61,0x37,0x3f,0xfe,0xa1,0x13,0xdf,0xf6,0x78,0xdd,0x83,0x49,0x51,
++		0x46,0xe6,0x5d,0x5d,0x19,0xe8,0x24,0x9c,0xd1,0x9d,0x2c,0xfb,0xea,0xff,0xde,0xbd,
++		0xc6,0x65,0x77,0xc0,0x8e,0x06,0x6f,0x9d,0x98,0x33,0x42,0x8c,0x7f,0xc4,0x33,0x0e, /* 1440 */
++		0x1e,0x59,0x7e,0x90,0xa5,0x84,0x34,0xb6,0xff,0xc7,0x73,0x68,0xf7,0x4d,0x33,0x08,
++		0xcc,0x78,0x67,0x12,0x52,0xc1,0xc1,0xf6,0x57,0x49,0x47,0x59,0x9f,0x30,0x76,0x6a,
++		0xa6,0xb9,0x8c,0x5a,0x21,0x92,0x34,0x7c,0x7b,0xd9,0xe2,0x3c,0x46,0x6a,0xae,0x2d,
++		0xbe,0x09,0x5f,0x6a,0xc0,0x7c,0x2f,0xe6,0xc0,0xd3,0x0c,0x3c,0xa4,0xe2,0x8f,0x5c,
++		0xac,0xfd,0x5c,0x2b,0x25,0xfe,0x42,0x76,0x89,0x0f,0x82,0x08,0xa1,0xcc,0xbc,0x39, /* 1520 */
++		0x24,0x20,0x1b,0x7f,0xf6,0xe9,0xbd,0x33,0xac,0x10,0x0d,0x57,0xfd,0xcd,0x6c,0x38,
++		0x5d,0x76,0xca,0x21,0xfa,0x62,0xde,0x57,0x76,0xaa,0x0d,0xe6,0xdc,0xad,0x26,0xe0,
++		0xda,0x4a,0x8e,0x26,0x46,0x85,0x3a,0xc0,0x4a,0x04,0x28,0xf5,0xf9,0xfe,0xd8,0xe9,
++		0x66,0x08,0x5a,0x8b,0x99,0xee,0x0c,0x19,0xa0,0x4a,0xec,0x82,0x1f,0x37,0xd3,0xef,
++		0x13,0xf7,0x2a,0x1c,0x08,0xc1,0x32,0x06,0xfd,0xaf,0xea,0x43,0xfe,0x86,0x2a,0xd6, /* 1600 */
++		0x4e,0x10,0x51,0x68,0x28,0x75,0x2a,0x4f,0x3f,0x64,0x14,0x3e,0x34,0x48,0x66,0x3d,
++		0x60,0x9e,0xa2,0xdf,0x45,0xf6,0x76,0xf0,0xdd,0x3c,0xbd,0x23,0xfd,0x40,0x16,0x5e,
++		0xbf,0xc6,0xa9,0x13,0xcc,0x28,0x21,0x3b,0x47,0x71,0x7f,0x41,0xf1,0x9f,0x76,0xba,
++		0x18,0x1c,0x90,0x27,0xa5,0xb3,0x42,0xfb,0x7a,0xff,0x5a,0xc9,0xc0,0x17,0xb2,0x88,
++		0x81,0x10,0x0e,0x66,0x63,0xcb,0xfb,0xc1,0xa5,0x2f,0x09,0xc5,0x6d,0xb0,0x03,0x80, /* 1680 */
++		0x50,0x6e,0x88,0x70,0xa6,0xa4,0x29,0xfb,0x2e,0x59,0xf4,0x62,0x84,0x89,0xdd,0x28,
++		0x91,0xee,0x7a,0x98,0xd8,0xdf,0x7d,0x61,0xac,0xe8,0x2c,0x9d,0xf6,0xb1,0x24,0xc6,
++		0x95,0xfa,0x2e,0xe7,0x40,0x39,0x36,0x62,0x96,0x1e,0x21,0x7e,0x94,0x1b,0x5c,0x25,
++		0xd6,0x8e,0x08,0x22,0x67,0xe9,0x9d,0x99,0xbe,0x92,0xee,0x85,0x6d,0x07,0x7c,0xe0,
++		0x8a,0xcb,0x8b,0xfe,0x7a,0x0c,0x0a,0x2e,0xb4,0x41,0xea,0x2b,0xc2,0x07,0x39,0x26, /* 1760 */
++		0x18,0x69,0x40,0xd7,0x61,0x7d,0x10,0xec,0x59,0x7f,0xc6,0x8f,0xe8,0xa5,0x69,0x6f,
++		0x00,0x75,0xf8,0xd0,0x7e,0x9b,0xc7,0x88,0x92,0x7b,0x7f,0x39,0xb3,0x01,0x80,0x82,
++		0x14,0x54,0x63,0x74,0x02,0x15,0xee,0xe0,0x8d,0xe2,0x59,0x45,0x66,0x92,0xf4,0xc0,
++		0xa5,0x9e,0xd6,0xcd,0x52,0x8d,0x5d,0xfc,0xbb,0x9f,0x40,0x8c,0x0b,0x54,0xd0,0x58,
++		0x34,0x4f,0x00,0x11,0x01,0x4f,0x3b,0x1b,0x1d,0x2b,0x63,0x22,0x69,0x09,0x21,0x72, /* 1840 */
++		0x77,0x47,0xc1,0x3d,0x8b,0x8c,0xad,0x2d,0xf6,0x17,0x60,0x8a,0x80,0x37,0xe9,0x01,
++		0x35,0x70,0x2c,0xd3,0x8a,0xa1,0xa4,0xfa,0xb5,0x02,0xe3,0xfe,0xf4,0xe5,0xf5,0x0a,
++		0x1d,0x72,0x25,0x62,0x71,0xe5,0xb4,0x11,0xa5,0xac,0xb3,0x8a,0x6c,0xd4,0x9e,0x44,
++		0x0c,0xc5,0x5c,0x77,0x35,0x78,0xd1,0x9b,0xc5,0x89,0x44,0x35,0x70,0x8a,0xa3,0xb2,
++		0x83,0xdc,0x2e,0x4f,0x33,0x5f,0xd5,0xbd,0x08,0x1a,0x84,0x88,0x4a,0x37,0x3c,0xa3, /* 1920 */
++		0x68,0x69,0x9a,0x6c,0x4b,0x62,0x82,0xf3,0x77,0xcc,0x31,0x64,0x1a,0x30,0xfc,0x27,
++		0xba,0x34,0xdb,0xb9,0x71,0x21,0x0f,0x46,0xaf,0xc0,0x28,0x36,0x8b,0x55,0xcb,0xb9,
++		0x41,0x49,0xfc,0x25,0xe3,0xdd,0xb1,0x2d,0x9c,0x7a,0x04,0x17,0xbf,0x89,0x5b,0xf9,
++		0x8d,0x62,0x99,0xb5,0x5c,0xec,0x7c,0x71,0xf8,0xb4,0x70,0x5b,0xdc,0x22,0xba,0xcc,
++		0xb7,0x2e,0x62,0xff,0x40,0x76,0xff,0x46,0x16,0x27,0x0c,0x11,0xfb,0x04,0x5f,0x5e, /* 2000 */
++		0x3a,0x34,0x28,0xe4,0x7f,0x61,0x9c,0xa4,0x2d,0x47,0x7c,0x1b,0xa9,0xa2,0xb0,0xd3,
++		0x04,0x1e,0xd2,0xe2,0x39,0x0c,0xf1,0x5c,0x1f,0x22,0x34,0xfe,0x3e,0xf3,0x8b,0x63,
++		0x43,0x6b,0xfc,0x50,0x26,0xee,0xfd,0x4a,0x5a,0x9a,0x6e,0x30,0x23,0xc9,0x6a,0xd0,
++		0x82,0xbc,0x35,0xeb,0x66,0xbb,0xb1,0x26,0x84,0xed,0xe4,0xc9,0x30,0x93,0xd3,0x00,
++		0xcf,0xa6,0x23,0xf6,0x9f,0x69,0x83,0xd1,0xd7,0xaa,0xfe,0xf8,0x4f,0xf8,0xd0,0x6f, /* 2080 */
++		0xdf,0x19,0xfd,0x9f,0x77,0x29,0x43,0xb8,0xc8,0x36,0x98,0x85,0x53,0x87,0x1d,0x73,
++		0x6f,0x27,0x54,0xa5,0x42,0xd6,0x9d,0xe1,0x66,0x84,0xe0,0x6f,0x08,0xd6,0x93,0x4e,
++		0x6d,0x87,0x14,0x93,0x26,0xae,0xf1,0x7a,0x70,0xda,0xc2,0x5c,0x85,0xc4,0x3a,0xd6,
++		0xba,0x8c,0xef,0xf8,0x06,0x0f,0x04,0x38,0x20,0xb0,0x2c,0xfc,0x94,0x7d,0x44,0xc6,
++		0x27,0x01,0x54,0x6b,0x9c,0x23,0x3f,0x82,0x66,0x35,0xb5,0x46,0x2a,0x8a,0x90,0x8f, /* 2160 */
++		0xc0,0x3e,0x6f,0xcb,0x72,0xa7,0xe0,0xc4,0xaf,0x9c,0x17,0x40,0x1a,0x4a,0x82,0x00,
++		0x1d,0x04,0x2e,0xbc,0xe3,0x6b,0x67,0x54,0x94,0x1d,0xd4,0x1c,0x46,0xd9,0x99,0x53,
++		0x04,0x2b,0x31,0x58,0x7c,0x46,0x14,0x83,0xa9,0x82,0x8a,0xa6,0xef,0xb6,0x83,0x0a,
++		0xe4,0x5a,0x30,0xb0,0x07,0x9a,0x02,0xea,0x73,0x88,0x62,0xc3,0xf0,0x03,0x06,0x08,
++		0xe2,0xab,0x2f,0xdb,0x13,0x08,0xb2,0x54,0xec,0xca,0xe1,0xcc,0xc0,0xd6,0xc5,0x72, /* 2240 */
++		0xcb,0x82,0x7a,0x20,0xb2,0xbf,0xa9,0x38,0xcf,0xe3,0x02,0x0a,0xc3,0xc6,0x8a,0xe9,
++		0x4d,0x0e,0xf5,0x2c,0xc5,0x10,0x75,0xd1,0x33,0xe2,0x64,0x65,0x0d,0xfb,0x9f,0x59,
++		0x27,0x8b,0x36,0xf5,0x36,0x46,0xfe,0xff,0x3f,0x7a,0x42,0x26,0x3b,0x83,0xa5,0x58,
++		0xee,0xac,0x08,0xa5,0x12,0x65,0x22,0xe4,0x33,0x90,0x8f,0x76,0xb0,0x0a,0x4b,0xf4,
++		0xe7,0xb6,0x9f,0x7d,0x08,0xf8,0x8d,0x25,0x5c,0x82,0x93,0x48,0x46,0xd5,0xfb,0x95, /* 2320 */
++		0xfd,0x8e,0x64,0xf0,0x5b,0x70,0x7f,0x88,0xef,0x0d,0x4a,0xe8,0xa9,0x39,0x6d,0xe1,
++		0x7c,0xd4,0xcb,0x2a,0x7a,0x3d,0x64,0xd2,0x8e,0x61,0xbb,0x95,0xa7,0xb2,0xbb,0x20,
++		0x99,0x4a,0x0a,0x9b,0x3d,0xfb,0x27,0x3e,0xb5,0x91,0xab,0x5e,0xcb,0x2b,0x8d,0xac,
++		0xcf,0x7e,0xff,0xa9,0x4c,0xc1,0x9d,0x94,0x38,0x87,0xde,0x24,0xe1,0xc4,0xe1,0x93,
++		0xbe,0xb8,0x57,0x97,0x8b,0xd5,0x90,0x1f,0xba,0x38,0xbc,0x21,0xf1,0x4c,0x28,0xee, /* 2400 */
++		0x08,0x0a,0x6c,0xd7,0xfe,0x76,0xfc,0x22,0xd6,0xea,0x36,0xc0,0x88,0xe0,0x92,0xae,
++		0x67,0x90,0x19,0xe5,0xd6,0x40,0xcc,0xf8,0xf6,0x4f,0x19,0x2f,0xa5,0x34,0x16,0xa0,
++		0x1f,0x4a,0x38,0x49,0xcd,0x76,0xb9,0x4f,0x35,0x20,0x26,0x8b,0x15,0xce,0x60,0xdd,
++		0x2b,0xb9,0x86,0x71,0x00,0x4d,0x5d,0x2f,0x87,0x99,0x2e,0x32,0x87,0xfa,0xb3,0x70,
++		0x80,0x61,0x7d,0x12,0x7a,0xd3,0x62,0x8e,0xa4,0x1a,0x36,0x70,0xb5,0xfb,0xb2,0xc7, /* 2480 */
++		0x2d,0x5b,0xa1,0x11,0x3c,0x63,0x87,0x6f,0xfc,0x34,0xc2,0x8e,0x8f,0x86,0xdd,0x9d,
++		0xed,0x62,0x16,0x73,0x29,0xba,0xd1,0x8c,0xc8,0x77,0x90,0x30,0xb1,0x74,0x3e,0x44,
++		0xf3,0x01,0xee,0x5f,0xea,0x9b,0x69,0x51,0x92,0xff,0xc0,0xa4,0x28,0x4f,0x75,0x96,
++		0x03,0x06,0xb8,0x01,0xa7,0x89,0x96,0x20,0xc5,0x28,0xec,0x26,0x2e,0x36,0xab,0x65,
++		0xa5,0xa0,0xf7,0x34,0x2c,0x1e,0x7b,0x5b,0x12,0xda,0xd6,0x90,0xed,0x9e,0xeb,0x64, /* 2560 */
++		0x22,0xb2,0x45,0x27,0x85,0x6b,0x73,0x11,0x15,0x44,0x47,0x45,0xac,0xcf,0x88,0xc1,
++		0x0a,0x28,0x24,0x38,0x90,0x64,0x37,0x08,0x6e,0x1b,0xac,0x41,0xe8,0x53,0x96,0x23,
++		0x03,0xd1,0xe4,0xdd,0x0e,0x1a,0x72,0x8f,0x02,0x7c,0x0c,0x77,0xdf,0xa4,0xde,0x9e,
++		0xf6,0xec,0x8b,0x34,0x71,0x91,0xb4,0xcc,0x76,0xc7,0x22,0xc1,0x0b,0x36,0xa4,0x93,
++		0x71,0x8d,0x3e,0x0d,0x25,0x45,0xf2,0x3b,0x53,0x44,0x88,0x23,0x06,0xaa,0xb6,0x34, /* 2640 */
++		0x54,0x07,0x70,0x8f,0x5e,0x1e,0x58,0xac,0x9c,0xc8,0x7f,0x75,0x28,0x07,0x0a,0x2e,
++		0x96,0xea,0x54,0x55,0x0b,0x2e,0xbc,0x04,0x20,0x63,0x36,0x47,0xa7,0x76,0xec,0x56,
++		0x93,0x29,0xb5,0xf6,0x0f,0x1e,0xdc,0x07,0x35,0xfa,0x6e,0xc5,0x7a,0xc7,0xd0,0x4e,
++		0xd3,0xa9,0x45,0x6c,0x02,0x08,0x40,0xea,0x05,0x5a,0x30,0x36,0x5b,0xf5,0x44,0x2b,
++		0x6c,0xf2,0x3b,0xdc,0xbb,0xc4,0x3d,0x38,0xd6,0x52,0x62,0xde,0x13,0x12,0x44,0x06, /* 2720 */
++		0x99,0xee,0x86,0xa3,0xfa,0x21,0xd9,0xe7,0x59,0x5e,0xe1,0x92,0x68,0x93,0x6a,0x57,
++		0x99,0x02,0x47,0x59,0xdc,0x21,0x6a,0xf9,0x82,0x82,0x0a,0x14,0xd6,0x02,0x63,0x8d,
++		0xfc,0x8b,0xfb,0x33,0x79,0x9f,0xda,0xde,0x91,0x80,0xe3,0x73,0x1c,0xe2,0xfa,0xdb,
++		0xd2,0x69,0x38,0xcf,0x70,0x72,0xd0,0x68,0x36,0xbb,0x41,0xfe,0x5f,0x8b,0x05,0x30,
++		0xa1,0xe4,0xc1,0x92,0xfa,0xef,0x39,0xd5,0x42,0xaa,0x36,0x66,0xbf,0xab,0xf6,0x2f, /* 2800 */
++		0x76,0x3b,0x09,0x44,0x65,0x51,0xc4,0x26,0x6b,0x6a,0x9a,0xbd,0x58,0x7b,0xa4,0x92,
++		0xdf,0xe5,0x25,0xf8,0x5e,0x6a,0xa8,0x48,0xa0,0x8d,0xff,0x4a,0x51,0xc3,0xe6,0xfa,
++		0xa5,0x65,0x0f,0x5b,0x87,0xbb,0xe7,0x31,0x03,0xad,0xd7,0xaf,0x64,0x9a,0xfa,0x0b,
++		0x6e,0x03,0x5b,0x75,0x53,0x2d,0xa8,0x0c,0xa4,0xf5,0xc7,0xfd,0xc1,0xe9,0x6f,0x4f,
++		0x41,0x4f,0xa5,0x98,0x00,0x35,0x19,0x1a,0xc4,0xc2,0xa9,0xe9,0x32,0xe7,0x43,0x16, /* 2880 */
++		0x25,0xbb,0x54,0xd9,0x3e,0x76,0xd9,0xbd,0x05,0x78,0xb0,0x58,0xad,0x19,0x6f,0x59,
++		0x1f,0xc1,0xa7,0xb0,0x51,0x91,0x36,0x0c,0xf2,0x13,0xda,0xfd,0x28,0x3b,0xb7,0x36,
++		0xbe,0xcb,0x4c,0x68,0x17,0x03,0x1d,0x27,0xfb,0xae,0x20,0x54,0xf7,0x95,0x6d,0x9d,
++		0x74,0xd4,0x8a,0xf8,0xff,0xc3,0xee,0xa9,0x0a,0x46,0x0e,0xc1,0x22,0x3a,0x3e,0xd3,
++		0x19,0x3a,0x77,0xdc,0xcb,0x6f,0xbe,0x1f,0xf9,0x0d,0xdc,0xa6,0xa6,0x7c,0x00,0x02, /* 2960 */
++		0x6b,0xe6,0xf3,0xac,0x10,0x50,0x04,0x17,0x83,0x22,0xd1,0x76,0x8f,0xd0,0x26,0xbe,
++		0xf8,0xa9,0x54,0xdc,0xc5,0x8f,0x93,0x39,0xea,0x7b,0x01,0xf9,0x3e,0x01,0xf6,0xd8,
++		0x8f,0xcf,0xdc,0x21,0x9c,0xb1,0x7e,0x9f,0xb7,0x8e,0x95,0x33,0x83,0xf5,0x4e,0x34,
++		0x00,0xa2,0x9d,0xc4,0x13,0xd5,0xb6,0xcf,0xd5,0xec,0x2d,0xed,0x6b,0x4a,0xc7,0xf8,
++		0x29,0x93,0x8b,0x2e,0x6e,0xe4,0x4a,0xdc,0x0c,0xee,0xd1,0x43,0xc7,0xfd,0x06,0xa4, /* 3040 */
++		0xc2,0xe5,0xcf,0x8b,0x82,0xfb,0x37,0xb7,0x07,0xe5,0xf9,0x20,0x82,0xc3,0x8c,0x4a,
++		0xfc,0x37,0xa8,0x9c,0x6f,0xbd,0xde,0xd8,0xdd,0xc6,0x58,0x2a,0xf7,0xa2,0x9d,0xb9,
++		0x74,0x2d,0x46,0xa1,0x61,0x15,0xb8,0xd2,0x63,0x86,0x5b,0xa0,0xb7,0xa2,0xe1,0x84,
++		0xa7,0x70,0x11,0x8a,0xb7,0xad,0x7b,0x7e,0xa4,0xc7,0x87,0x87,0x51,0x9f,0xd9,0x01,
++		0x17,0x3e,0x19,0x67,0xd0,0xf8,0x6d,0xe3,0x1f,0xb0,0x68,0x63,0x9d,0xf1,0x93,0x1e, /* 3120 */
++		0xf8,0xb0,0xe0,0xf1,0xb8,0x9f,0xce,0x91,0x0f,0xa8,0x27,0x7c,0x83,0x84,0x9d,0xfe,
++		0xfc,0xa9,0x0f,0x04,0x02,0xcb,0xbe,0x77,0x5d,0x60,0x73,0x9e,0x66,0x0a,0x43,0x49,
++		0xdf,0x04,0x60,0x6c,0xd5,0x36,0x38,0x97,0x39,0xa8,0xdc,0xcc,0x54,0x2b,0x71,0xdd,
++		0x96,0xc9,0x57,0xe1,0xd3,0xdb,0x72,0x3a,0x5e,0x82,0x53,0x74,0xdb,0xa6,0xdf,0x92,
++		0x07,0xef,0x1e,0x36,0x63,0x38,0x9f,0x32,0x62,0x42,0xac,0xbf,0x9a,0xe2,0xaf,0xb2, /* 3200 */
++		0x21,0xdc,0x91,0xc1,0x2c,0xd8,0x51,0x5d,0x44,0xff,0x31,0x6a,0xda,0xbe,0x79,0xd4,
++		0x18,0x93,0xe9,0xcf,0x3d,0x87,0xb4,0xb6,0x01,0x68,0xa0,0x50,0x7d,0x14,0x6e,0x48,
++		0x2c,0x8c,0xe4,0xe6,0xea,0x7f,0xe3,0x55,0x9f,0x47,0xf2,0x92,0x0b,0xf0,0x27,0xde,
++		0x99,0xab,0x23,0x98,0xb7,0x6d,0x56,0x92,0x3d,0x68,0x03,0xe4,0x4e,0xcd,0x60,0xc8,
++		0x1a,0x2b,0xf8,0xf3,0xfe,0x9a,0x21,0x93,0xe7,0x60,0x70,0xbc,0xa7,0x62,0x1c,0xf2, /* 3280 */
++		0xe3,0xed,0x99,0xb8,0x4a,0x83,0x28,0x14,0x05,0xc2,0xc7,0xfc,0xd6,0xc5,0x0d,0xe9,
++		0xba,0xb6,0x24,0x2e,0xb0,0xc1,0x39,0x0a,0xfa,0xe9,0x25,0x9e,0xcc,0xa8,0xe4,0xb2,
++		0x40,0xe3,0xe0,0x04,0x41,0xba,0xbc,0x69,0x39,0x3e,0xa3,0x8f,0xf2,0x87,0xb9,0x12,
++		0xcc,0xae,0xc4,0x0a,0x56,0x77,0x43,0xf5,0x9f,0xbe,0x6f,0xc5,0xfd,0xd8,0x79,0xb0,
++		0x8b,0x0d,0x72,0xdb,0x96,0x68,0xd2,0x5e,0xd6,0x57,0x76,0x17,0x54,0x52,0x15,0x56, /* 3360 */
++		0x1c,0x12,0xed,0xe2,0xae,0x5a,0xd3,0x00,0x11,0x66,0x53,0xc1,0x82,0x56,0xaf,0xef,
++		0x65,0xf1,0x9c,0xa2,0x66,0x9b,0x92,0xfe,0x02,0x3a,0x12,0xf5,0x2d,0xe2,0x68,0xfe,
++		0x33,0xfc,0x17,0x72,0x32,0xe9,0x09,0x41,0x23,0x2c,0x7d,0xa5,0xee,0x2d,0xde,0x80,
++		0x1a,0xd4,0x18,0xb0,0xc4,0x01,0xbc,0x55,0x29,0x27,0xcc,0x70,0x8c,0x25,0x01,0xfc,
++		0x39,0x3e,0x31,0x1b,0xe3,0xaf,0x5c,0x61,0x73,0xd1,0x36,0xc9,0x2e,0x97,0x53,0x39, /* 3440 */
++		0x77,0x94,0xee,0xa9,0x82,0x1c,0xcc,0xad,0x98,0xae,0xa8,0xc9,0x32,0xd0,0x27,0xf9,
++		0xa8,0x79,0x6f,0xdc,0x9d,0x55,0x4b,0x4f,0xed,0xf1,0x74,0x53,0x2d,0xa4,0xdf,0xc5,
++		0x5c,0x9d,0x14,0x66,0xef,0x28,0xc8,0xcd,0xa6,0xb7,0x0b,0x17,0x3d,0x1e,0x09,0xe6,
++		0x63,0x7a,0x97,0xb8,0x94,0x03,0x51,0xbe,0x52,0xf5,0x08,0x6a,0x61,0x08,0x63,0x58,
++		0xd3,0x0b,0x18,0xa3,0x8d,0xc0,0x62,0x12,0x09,0x35,0xd1,0x6b,0xaf,0xeb,0xc4,0xe8, /* 3520 */
++		0x84,0x9a,0xfa,0x61,0xf7,0xff,0x81,0x61,0x22,0xb4,0xb7,0xad,0x35,0xa2,0xdd,0x7c,
++		0x82,0xbb,0x71,0xd5,0x82,0x6a,0x09,0xba,0xf8,0x88,0x51,0x1b,0x6b,0xf5,0xfe,0xf6,
++		0x36,0x8c,0x97,0xac,0x28,0x0d,0xe1,0xc9,0xfe,0x36,0xac,0x80,0xa4,0x74,0x2f,0xfb,
++		0x95,0x2b,0x99,0xd8,0x09,0xf9,0xa3,0x80,0x25,0x18,0x54,0x02,0x7b,0xe7,0x5b,0xbf,
++		0xaa,0xd5,0x1f,0x92,0x64,0x08,0x8c,0x7a,0xd4,0x5b,0x0b,0x99,0x18,0x95,0x9d,0xca, /* 3600 */
++		0x51,0x66,0x52,0x15,0x52,0x86,0xb9,0x2f,0x40,0x9b,0x07,0x47,0x0e,0x34,0x45,0xf8,
++		0x15,0x42,0x3a,0x56,0xae,0x83,0xbe,0x92,0x46,0x6b,0xf9,0x0a,0xed,0x3b,0xed,0xca,
++		0x23,0xf4,0x78,0x48,0x23,0xa9,0x4a,0x84,0x5c,0x35,0x2d,0x53,0x4d,0xdc,0xa0,0xd7,
++		0x31,0x2b,0x4f,0x61,0x7a,0xbf,0xa4,0x72,0x66,0x32,0x9b,0x27,0x61,0xea,0x7b,0x83,
++		0x01,0x5b,0x4d,0x8f,0x85,0x06,0xa0,0xfb,0xf0,0xb4,0xe5,0xd6,0x69,0xaa,0xb5,0x84, /* 3680 */
++		0x20,0x3f,0x96,0x4b,0x94,0x54,0x3a,0xa3,0xd2,0xe7,0xba,0xdf,0x09,0x26,0x47,0x21,
++		0x5b,0x57,0x65,0x75,0x85,0xdb,0x59,0xd4,0xe2,0x25,0x54,0x5a,0x4d,0x25,0xef,0x46,
++		0x10,0x32,0xea,0xb0,0x83,0x32,0x03,0x33,0x1f,0x0e,0x0b,0xda,0x60,0x52,0x20,0xbc,
++		0xa6,0x57,0x24,0xc0,0xc5,0x27,0xa8,0xd5,0x41,0x41,0xe5,0x4b,0x9a,0xcd,0xe3,0xa2,
++		0x8e,0x43,0x0c,0x65,0xd0,0xb0,0x91,0x54,0x40,0xbc,0x33,0x24,0x2d,0xed,0x53,0x25, /* 3760 */
++		0x8b,0xbb,0x70,0x49,0x52,0x14,0x23,0x64,0xbd,0x2e,0x52,0xca,0x02,0x2b,0xe7,0x3c,
++		0xc1,0x29,0x9a,0x8e,0x72,0xfc,0x75,0x13,0xae,0xa8,0x9b,0xfd,0x16,0xa6,0x1e,0x4a,
++		0x50,0x47,0xb8,0x11,0x5b,0xe5,0xdb,0x2b,0xfe,0x69,0xdb,0xcb,0x43,0xfa,0x4f,0x0f,
++		0x37,0x07,0x91,0x68,0x84,0xb0,0x85,0x80,0x20,0x98,0x13,0x45,0xec,0xfd,0x2d,0x9f,
++		0xc2,0x61,0xb1,0x25,0xeb,0x2f,0xc6,0x6d,0x3c,0xcd,0x0a,0x42,0xa6,0x19,0x6f,0x25, /* 3840 */
++		0xb8,0x1d,0x92,0x3c,0x36,0x08,0x4d,0xc9,0x80,0xc2,0xa6,0xa6,0x65,0xbd,0x12,0x28,
++		0xab,0x47,0x5a,0x90,0x64,0xfe,0xd8,0x46,0xa2,0x60,0xc3,0x51,0xa1,0xcc,0xe0,0xa5,
++		0xe7,0x03,0xe1,0x5c,0xbf,0x58,0x94,0xd6,0x2b,0x99,0xdc,0xca,0xcb,0x5c,0xa3,0x0b,
++		0xce,0x77,0xd7,0x9a,0xf6,0x0f,0xfd,0xa7,0xb3,0xfe,0x18,0xb0,0xbf,0x9f,0xa3,0xeb,
++		0x43,0x1c,0x26,0x70,0x7b,0x7e,0xac,0x0d,0x6e,0xf4,0x69,0x07,0x39,0x61,0x4f,0x72, /* 3920 */
++		0x8d,0x7d,0x43,0xb4,0x8a,0xa4,0x32,0xc5,0xf1,0x81,0xb8,0x86,0x28,0xa4,0x84,0x58,
++		0x61,0x56,0xa7,0x18,0xf7,0x8f,0xda,0x00,0x1b,0xb8,0x54,0x43,0xe8,0x2b,0xa7,0xbd,
++		0x3c,0x57,0xb8,0xeb,0xa9,0xff,0x8a,0x58,0x74,0xb7,0xaa,0xc2,0x09,0x7f,0x63,0x49,
++		0x79,0x4c,0xa0,0xb3,0xa6,0x49,0x8e,0x25,0x9d,0x31,0x32,0xf9,0x69,0xce,0xf5,0xf1,
++		0x5b,0xd1,0x7e,0x8d,0x61,0x2c,0x0f,0xf6,0xf2,0x3e,0x26,0xad,0xaf,0xbc,0x72,0x06, /* 4000 */
++		0x4a,0x68,0x78,0x8d,0xe4,0x43,0xfd,0x3e,0xa5,0xec,0x3c,0x76,0x3a,0x77,0x1e,0x66,
++		0xc6,0x81,0x24,0x4b,0x02,0xf6,0xc0,0xb0,0x97,0xde,0x9c,0x13,0x11,0xff,0xc9,0x53,
++		0x79,0x56,0x07,0x18,0x22,0x0d,0xa2,0x4b,0x94,0x46,0x93,0xd8,0xac,0xb9,0x8f,0x53,
++		0x98,0x3a,0xe7,0x96,0xed,0x5d,0xd6,0x46,0x37,0xd8,0xf1,0x31,0xdb,0x76,0xad,0x60,
++		0x97,0x3c,0xb4,0xb9,0x30,0xac,0x1e,0xb9,0x97,0x87,0x4f,0x3d,0x70,0x72,0x47,0x43, /* 4080 */
++		0x43,0xc0,0x95,0x26,0x31,0xcd,0xd6,0x23,0x1c,0xf8,0xe9,0xa6,0x05,0x23,0x15,0x37,
++		0x1f,0x3a,0x69,0x0c,0xe1,0xa2,0xea,0x32,0xcc,0x54,0xb3,0x41,0xbb,0xe8,0x42,0x78,
++		0x16,0x41,0xec,0x38,0xa8,0x87,0x7b,0x0c,0x7e,0x65,0x0c,0xa9,0xff,0x05,0x33,0x9e,
++		0x44,0x26,0x24,0xa3,0x27,0xf0,0xb8,0x94,0x12,0x2c,0x7b,0x80,0xfd,0xc9,0x61,0x01,
++		0x1e,0x41,0x80,0x91,0x24,0x63,0xf6,0x79,0xdf,0xd7,0x93,0x46,0x57,0xa6,0x02,0x48, /* 4160 */
++		0x5b,0xf8,0x2a,0x7f,0x53,0x52,0x24,0x42,0x8b,0x04,0xcb,0xcd,0xbb,0x3b,0x89,0xe6,
++		0xe9,0x2b,0x31,0xc1,0x60,0xce,0xb5,0x62,0x0d,0xe1,0x85,0x1d,0x4b,0xdc,0xd3,0x42,
++		0xc6,0x50,0x12,0xb4,0x93,0xed,0x60,0xc7,0x25,0x5d,0x3a,0x61,0xf0,0x34,0x78,0xeb,
++		0x66,0xe1,0xf6,0xc1,0x06,0xd3,0x2a,0x64,0x4a,0x63,0xc1,0x6a,0xed,0x72,0xec,0x9d,
++		0x3a,0x3f,0x7b,0xe0,0x40,0xed,0x17,0x36,0x2e,0x57,0xa6,0x2d,0xdb,0xb2,0x06,0x5d, /* 4240 */
++		0x0c,0x3f,0xf7,0x47,0x3d,0xbe,0xf9,0x2c,0x0f,0x1b,0xea,0x35,0x7e,0xbf,0x52,0xf4,
++		0x6a,0xa1,0x70,0x09,0xb3,0xb6,0xa8,0xe4,0x6e,0x16,0x58,0x3a,0xf3,0xd0,0x31,0x24,
++		0x41,0x42,0x04,0xdf,0x4b,0xe8,0x5d,0x5a,0x4d,0xbe,0xa5,0x3b,0x29,0x60,0xa8,0x9d,
++		0x94,0x60,0x94,0xcd,0xf4,0x40,0x25,0x8b,0x40,0xc2,0xab,0x14,0x81,0x52,0x33,0x30,
++		0xe8,0x51,0x38,0xf5,0xc1,0x77,0xe4,0xf6,0x32,0xcd,0x06,0xf8,0xe4,0x93,0xc5,0xd9, /* 4320 */
++		0x59,0x3e,0x56,0xf5,0x24,0x99,0x64,0xa9,0x7c,0x95,0x8b,0x8d,0x79,0x20,0x03,0x18,
++		0x99,0x3e,0xd2,0xa5,0x80,0xa5,0xea,0x42,0xa6,0x9b,0xa2,0x11,0x1e,0xc2,0x3d,0xde,
++		0x55,0x56,0x44,0xdc,0xc2,0x4b,0xe8,0x4a,0x4a,0x80,0xca,0xd5,0xfe,0x70,0x80,0xac,
++		0xa0,0xa4,0x3d,0x0b,0x47,0x62,0x0c,0xdb,0xb1,0x94,0x77,0x00,0xba,0x64,0xbc,0x77,
++		0x0d,0xe0,0xbf,0x71,0x19,0xd5,0x1e,0x0c,0xa4,0x5c,0xf1,0xb2,0x89,0x53,0x22,0xaf, /* 4400 */
++		0xd6,0xca,0x83,0x1c,0x61,0x32,0x05,0x91,0x1f,0xcb,0x63,0x8c,0x73,0xc4,0x37,0x31,
++		0xe0,0xee,0x9d,0x3d,0x17,0x6a,0x41,0x81,0x80,0x27,0x9e,0xed,0xd4,0xfc,0x9c,0x11,
++		0x88,0x5d,0x93,0xf7,0x68,0x20,0xed,0x42,0x76,0x46,0x52,0xa5,0xf5,0xa1,0x93,0x5e,
++		0x56,0x19,0xc9,0xc3,0xf5,0x60,0x42,0x42,0x09,0xdf,0xd8,0x6e,0x38,0x10,0x68,0x9e,
++		0x20,0x14,0xf1,0xef,0x18,0xff,0x50,0xb6,0x1f,0x88,0x7c,0xd9,0x5c,0x4d,0x7c,0x6b, /* 4480 */
++		0x0b,0x3c,0xd2,0x4c,0xd6,0xe8,0xef,0xf3,0xf8,0x51,0x22,0xa1,0x31,0x9d,0xf2,0x8b,
++		0x8b,0xdc,0x96,0x93,0xd5,0x02,0x2c,0x1c,0x7c,0x7b,0xa3,0xa2,0x25,0x93,0xef,0x4d,
++		0x20,0x61,0x0d,0x48,0xf1,0xc8,0x09,0x37,0x32,0x54,0xb9,0xc8,0x41,0x02,0xef,0x72,
++		0x4a,0x26,0x82,0xbb,0xb0,0x64,0x90,0x0e,0x8c,0x3e,0x1d,0xde,0xeb,0x4b,0x4b,0x57,
++		0x47,0xe7,0x05,0x08,0x4f,0x55,0x72,0x74,0x7e,0x90,0x95,0xad,0xb6,0xd9,0xe8,0x02, /* 4560 */
++		0x0f,0x3a,0x43,0x7c,0xc4,0xed,0x65,0x7b,0x8b,0xa5,0xad,0xe4,0x24,0x39,0x3a,0x3b,
++		0x5c,0xa1,0xab,0x07,0x09,0x19,0x45,0x63,0x79,0xa0,0xba,0xe3,0x09,0x0e,0xa5,0xa9,
++		0xb0,0xbb,0xcd,0xf0,0xfa,0x07,0x23,0x24,0x34,0x35,0x6c,0xae,0x00,0x00,0x00,0x00,
++		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x0f,0x16,0x1c,0x23,
++		0x2c,0x35,0x3c),                                                                 /* 4627 */
++	  chunk_from_chars(
++		0xd6,0x61,0xcc,0x6d,0x9c,0x94,0xc0,0x01,0x3d,0xab,0x38,0x73,0xee,0xa0,0x04,0x9d,
++		0x47,0x1c,0xd2,0xfd,0xa9,0x50,0x6f,0x14,0x48,0x14,0xf5,0x3f,0x12,0x60,0xa9,0x19,
++		0xe9,0x8b,0x7d,0x19,0x1d,0xd4,0x15,0x42,0x27,0xdd,0x01,0x51,0x97,0xcb,0x5c,0x35,
++		0x28,0x75,0xf6,0x7d,0xa3,0x71,0xad,0xad,0xc0,0xed,0x56,0xfb,0x5e,0x6d,0x40,0x87,
++		0x18,0x17,0x1e,0xc8,0xe4,0x35,0xd2,0x13,0xc1,0x60,0xd1,0x1c,0x7e,0xd2,0xf6,0x35, /*   80 */
++		0x1f,0x97,0x35,0xdc,0x25,0x25,0x79,0xd5,0x42,0xc5,0x83,0x05,0xf0,0xd8,0x36,0xf8,
++		0x6b,0xd1,0x9d,0x9f,0x9f,0x7e,0x3d,0xa5,0x3d,0xcd,0xd0,0x58,0x6a,0x1e,0x4f,0x93,
++		0x11,0x06,0xd2,0xc8,0x90,0x28,0x66,0x73,0x29,0x79,0xa3,0xd3,0x79,0x3d,0xbb,0xc6,
++		0xf3,0xa6,0xcf,0x25,0x52,0x0d,0x21,0x38,0x12,0x02,0x2a,0x7e,0x06,0x11,0x78,0x7c,
++		0x79,0xf5,0x65,0x7b,0xfa,0xbc,0x5d,0x51,0x56,0xf9,0x62,0x2d,0xd7,0x11,0x46,0x66, /*  160 */
++		0xc0,0xb7,0xba,0x30,0x22,0x8a,0x29,0xb4,0x9a,0xdb,0x5c,0x6d,0x2a,0xf0,0x39,0x65,
++		0xd2,0xc5,0xf5,0xd6,0xbc,0x16,0x88,0x5f,0x95,0x8e,0xb2,0x4a,0x08,0x25,0x50,0xc9,
++		0x35,0xe8,0x89,0x9a,0x88,0xbc,0x75,0x7f,0xfc,0x56,0x3e,0x83,0x39,0x02,0xd7,0xc9,
++		0x8d,0xc3,0xa1,0x97,0x4c,0x93,0x68,0xbe,0x6b,0xee,0xc1,0xa0,0x15,0x46,0x5d,0xae,
++		0x7a,0xfe,0x34,0x4d,0x63,0x25,0x02,0x30,0x69,0x15,0x1b,0xf0,0xe6,0x88,0x6b,0x6f, /*  240 */
++		0x87,0x5a,0x89,0xb8,0xda,0xba,0x5a,0x47,0x08,0xea,0xcb,0x9f,0x93,0xec,0xa8,0xa0,
++		0xa4,0x04,0x26,0x9e,0x99,0xd6,0x1f,0x76,0xc2,0x90,0x90,0x58,0x25,0x43,0xa2,0x1c,
++		0xa3,0x1b,0xa6,0x77,0x9d,0xd6,0x33,0xa0,0xd4,0x40,0xb5,0xe0,0xcd,0xd2,0x96,0xac,
++		0x75,0xd0,0xe5,0x31,0x69,0x76,0x85,0xdd,0xb7,0x49,0xa1,0x8d,0x05,0x58,0xe2,0x90,
++		0xdd,0x0a,0xee,0x5f,0x3c,0x59,0xa2,0xbf,0x53,0x29,0x0b,0xbb,0x7e,0x8e,0xd6,0x1d, /*  320 */
++		0x2e,0x7d,0xa3,0xe8,0x3c,0x3b,0x23,0x20,0x59,0x7c,0x00,0x2b,0xde,0xcb,0xd8,0xae,
++		0xf7,0x78,0xd7,0x01,0x9c,0x2f,0x1f,0x98,0xbd,0xba,0x01,0xba,0x49,0x38,0x0d,0xc4,
++		0xa8,0x9b,0x50,0x0d,0x20,0xb5,0x5a,0x36,0x3f,0x5b,0xfb,0xdf,0xbb,0xd5,0x2d,0xa0,
++		0x25,0x5f,0x14,0x76,0xc0,0x8e,0x14,0x38,0xe9,0x8d,0x73,0xa7,0x8c,0xe5,0xd3,0x38,
++		0x49,0x50,0x36,0x18,0xf5,0x66,0xdd,0x2d,0x3d,0x4e,0x17,0xca,0x84,0x8b,0x2a,0xb3, /*  400 */
++		0xd5,0x86,0x0b,0x96,0xb8,0xf9,0x0c,0xa0,0x6a,0xd5,0xcf,0x73,0x20,0x9a,0x0b,0xd3,
++		0xb8,0xfa,0xcb,0x50,0x74,0x23,0xf1,0x34,0x7c,0x67,0x8e,0xbf,0x37,0x27,0x1f,0x49,
++		0xa0,0x6e,0x52,0xe2,0xf0,0x09,0xc4,0x1c,0x8f,0xe3,0x07,0xe6,0xcd,0xcc,0x20,0x57,
++		0xe1,0x23,0x7d,0xd0,0x71,0x03,0x55,0x78,0x57,0xcb,0x31,0x61,0xfc,0xbf,0xe7,0xdb,
++		0xf4,0x38,0x72,0x3e,0x22,0x15,0x6f,0x18,0x7b,0x0a,0x3a,0x48,0xea,0x85,0x43,0xe6, /*  480 */
++		0x66,0x2f,0x61,0x35,0x9c,0x6f,0xa4,0x95,0x02,0x48,0x45,0x72,0xf1,0xe4,0xad,0x3f,
++		0x82,0xf6,0x49,0xc5,0x78,0x18,0x85,0xbc,0x13,0xf3,0x84,0x2b,0xfb,0x5f,0xc2,0xa0,
++		0x9a,0x98,0x22,0x5a,0x4e,0x3f,0x8c,0xa6,0x31,0x89,0x98,0x3f,0xcf,0x92,0x0d,0x01,
++		0x32,0x56,0x67,0xa3,0x18,0x1e,0x71,0x6a,0xca,0xa3,0x81,0x61,0xbf,0x16,0xcb,0xab,
++		0x40,0x6f,0x92,0x20,0xc2,0x05,0xa8,0x74,0xbb,0x54,0xef,0x54,0x62,0x31,0x1b,0xad, /*  560 */
++		0x1d,0x97,0xa4,0xb7,0x2d,0x8d,0xb3,0x79,0xfb,0xaf,0x4b,0xb2,0xb2,0xd6,0x51,0x75,
++		0xc0,0xa8,0xeb,0xdc,0x53,0x8a,0x60,0x88,0xa6,0xdf,0xc9,0x5c,0x92,0xad,0x2b,0x26,
++		0x29,0x2b,0xc8,0x67,0x7e,0xb1,0xe6,0x4a,0xa7,0x12,0x63,0xa0,0xe1,0xc4,0x4e,0x72,
++		0xf7,0x6a,0xd3,0x8e,0x99,0x76,0xca,0xbe,0x17,0x88,0x53,0xd7,0x29,0xdc,0xe8,0x2d,
++		0x3f,0x7b,0x13,0x9a,0x54,0xd4,0x37,0xfc,0x26,0xfa,0x58,0x77,0x03,0xc0,0xb0,0xfe, /*  640 */
++		0x02,0x47,0x54,0x53,0x33,0x9b,0x66,0x4a,0x9a,0x6f,0xba,0x0b,0xad,0xf9,0xf2,0x14,
++		0x2c,0xec,0x12,0x1b,0xe3,0xd5,0xa9,0xc4,0x42,0x10,0x3e,0xbd,0x89,0x23,0x3c,0xff,
++		0xad,0x8d,0x5d,0xd6,0xd9,0x5c,0x8c,0xec,0x00,0x50,0x64,0x3a,0xb9,0xda,0xc3,0x66,
++		0x3b,0x2b,0x59,0x68,0xb6,0x54,0xd6,0x1b,0xfe,0x79,0xd6,0x65,0xe0,0x3f,0xeb,0x05,
++		0xe2,0x33,0x10,0x25,0xa5,0x15,0x5d,0x8b,0x1e,0xbd,0x5b,0xb0,0x0e,0xfe,0xf7,0x4b, /*  720 */
++		0x01,0x8d,0xa9,0xbe,0x9d,0x77,0x4c,0x6d,0xc9,0xdf,0x17,0xcb,0x9d,0x86,0x62,0xc0,
++		0xa7,0x66,0xf4,0x3d,0x43,0xde,0xb6,0x1c,0x92,0xf9,0xc1,0x7b,0x5f,0x9b,0x4b,0xbc,
++		0xb1,0x12,0x45,0xe6,0x95,0x93,0xa5,0xc2,0xfd,0xab,0xaa,0x8c,0x16,0x6d,0xac,0x32,
++		0xc9,0xb1,0x79,0x2a,0xd7,0xf0,0xcf,0xb7,0x68,0x98,0x6e,0x6e,0xad,0x60,0x0f,0x39,
++		0x63,0xe2,0x03,0x62,0x06,0x3e,0xf8,0x85,0x77,0x53,0xbd,0x96,0xa0,0xa4,0x35,0x51, /*  800 */
++		0x13,0xf3,0xa7,0x02,0xb6,0x5c,0xc8,0x0f,0x69,0xa8,0x06,0x14,0x51,0xa6,0xac,0xdf,
++		0x00,0x8f,0xfa,0xf8,0x3a,0xab,0x78,0xcd,0xeb,0x39,0xb7,0xf3,0x81,0xa5,0x48,0xde,
++		0x67,0xba,0x74,0xeb,0x93,0x70,0x0f,0x68,0x2e,0x9c,0xec,0x25,0x12,0x55,0x5f,0x7b,
++		0x80,0x50,0x85,0xa1,0xcd,0xf5,0x90,0xec,0x87,0xcf,0x1f,0x7c,0x58,0x6d,0x20,0x94,
++		0x6a,0x89,0x4c,0xfc,0xd8,0x60,0x2b,0x96,0x88,0xf3,0x02,0x5f,0xa1,0x6c,0x35,0x66, /*  880 */
++		0x14,0x0b,0x47,0xc5,0x34,0x63,0xc0,0x75,0xfb,0xd0,0xa3,0x67,0x83,0x0b,0x52,0x4c,
++		0xbe,0x4d,0x1a,0xe8,0xac,0xd3,0x5c,0x3e,0x63,0xe5,0x47,0x56,0xbe,0x1b,0x87,0x85,
++		0xd3,0x23,0xb3,0xae,0x64,0xc0,0x98,0x4f,0x52,0xbe,0xa9,0x56,0x3f,0x8b,0x6f,0xf1,
++		0x5a,0xb8,0xaf,0x8d,0xe7,0xd0,0x07,0x1e,0x4f,0xcf,0xec,0xf8,0xf7,0xb4,0x47,0x78,
++		0x0c,0x27,0x8f,0x59,0x69,0x85,0xd8,0x40,0xcb,0x6c,0xed,0x27,0xf5,0xea,0x08,0x54, /*  960 */
++		0x8f,0x50,0x67,0xb3,0x52,0x2b,0x2c,0xc0,0xd9,0xf6,0x5c,0x61,0x2b,0x73,0xf3,0xd6,
++		0xdc,0x16,0x1f,0x05,0xc8,0xbf,0xc1,0xb9,0xff,0x9c,0xca,0x8d,0xbb,0x23,0x72,0x40,
++		0x86,0x3e,0xe7,0xf0,0xaf,0x1a,0xe4,0x11,0x2d,0x24,0xad,0x63,0xcf,0x12,0x24,0x47,
++		0xf8,0xf8,0xaf,0xea,0x28,0xa2,0x0c,0x2f,0xe8,0x61,0x81,0xa6,0x8e,0xd1,0x43,0xf9,
++		0x1f,0x5e,0x29,0x97,0x86,0x6b,0xa0,0xfb,0x8b,0xa2,0x39,0x29,0xa3,0xaf,0x53,0x31, /* 1040 */
++		0x1b,0x4c,0x53,0x81,0xf3,0x73,0xc1,0xb7,0x22,0x50,0xf5,0xa8,0xd4,0xb9,0xfa,0x81,
++		0x30,0x52,0xfd,0x12,0xee,0x83,0x3a,0xc0,0x3e,0x5a,0xb3,0x0f,0x1c,0x33,0x92,0xf1,
++		0xdb,0x2f,0xf2,0xeb,0x0c,0xb0,0x11,0x2b,0xeb,0x4b,0xc0,0x45,0x59,0x37,0xc0,0x87,
++		0xff,0x8c,0x13,0x3a,0x00,0x0d,0x82,0x13,0xee,0x23,0xb0,0x7d,0xfb,0xc2,0x38,0xdc,
++		0xa2,0xd8,0x7d,0x16,0xfe,0x91,0x68,0x9c,0xf1,0x68,0xd7,0xca,0x0f,0x4b,0xbc,0xc3, /* 1120 */
++		0xb7,0xf2,0x08,0xa7,0x31,0x73,0xf1,0x4e,0x11,0x83,0xbd,0x67,0x10,0x66,0x6d,0xcf,
++		0xc4,0x0e,0x9d,0x2c,0xf2,0x79,0x55,0xab,0x7f,0x19,0x37,0x45,0x05,0x98,0x8e,0xbd,
++		0xad,0xe5,0x03,0xbe,0xef,0xed,0x83,0x86,0x40,0x46,0xa5,0x0b,0x5c,0x31,0x3f,0xdd,
++		0xdb,0xb3,0x30,0xc1,0x86,0xe5,0xf8,0xb3,0x9e,0x54,0xdc,0xba,0xe9,0xf9,0xc7,0x96,
++		0xa5,0x27,0xad,0x3d,0x19,0x48,0xd5,0x7b,0x01,0x62,0xa9,0x1b,0x82,0xa0,0xe8,0xbf, /* 1200 */
++		0x76,0x00,0xe1,0xf3,0xf7,0x52,0xf0,0x48,0x40,0x36,0x6e,0x45,0x74,0xfd,0x2b,0xbb,
++		0xec,0x8e,0x8f,0x7d,0x9d,0x1c,0xbc,0xbc,0x18,0x3b,0xc9,0xfc,0xab,0xe7,0x09,0x46,
++		0x86,0x57,0x62,0x5f,0x24,0xa5,0xc8,0x20,0x0d,0xa4,0xaa,0x74,0x40,0x49,0xc7,0xcb,
++		0xbf,0x2b,0x3f,0x0c,0xf1,0x41,0x89,0x60,0xd3,0x62,0xac,0xb8,0x97,0xdb,0x9a,0xff,
++		0x16,0x14,0x77,0xe4,0x41,0x63,0x37,0xbf,0x87,0x4b,0x25,0xdc,0x11,0x95,0xa0,0xc8, /* 1280 */
++		0x95,0x4d,0x60,0x22,0x77,0x33,0x27,0x10,0x3b,0x1c,0xa2,0x40,0x62,0x77,0xab,0xd1,
++		0x39,0x86,0x5f,0xad,0x95,0x26,0xdc,0xb6,0x4e,0x50,0x02,0x88,0x13,0x7f,0x64,0x60,
++		0x59,0x1c,0x1f,0x38,0xde,0x13,0xd9,0x71,0x57,0x4b,0x99,0x70,0x37,0xff,0x8f,0xed,
++		0xc2,0x93,0x5a,0x94,0xde,0x83,0xb0,0x82,0x24,0x98,0x5d,0x0d,0x8b,0x1c,0x56,0xaa,
++		0xe9,0xba,0x86,0xd3,0x3c,0xc6,0xbc,0x87,0x96,0x13,0x9e,0xa6,0x67,0xe3,0xc4,0x04, /* 1360 */
++		0x0c,0x5f,0xc7,0x30,0x88,0x33,0xc5,0xc2,0xd7,0xe0,0x20,0xba,0xf7,0x82,0x07,0xdf,
++		0xad,0x55,0xdf,0x5a,0x2f,0x64,0xa9,0xad,0x42,0xb6,0xc5,0xad,0x23,0xad,0x38,0xb6,
++		0x65,0x42,0x0e,0x32,0xa1,0xd7,0x02,0x0c,0x69,0x5e,0xb9,0xb1,0x9f,0x71,0x0e,0x95,
++		0x67,0xb3,0xe4,0xe5,0x8d,0x1b,0xd0,0x7a,0x4e,0x7f,0x15,0x6e,0xfd,0xdc,0xad,0x84,
++		0xed,0x66,0x46,0x02,0xf2,0xf1,0x90,0xee,0xb8,0x80,0xa6,0x9e,0xf3,0xdc,0x30,0x37, /* 1440 */
++		0xf2,0xb2,0xc8,0x89,0xcf,0x49,0xc6,0x79,0x41,0xd2,0xde,0x33,0xd7,0xd7,0x93,0x56,
++		0xfb,0xf1,0x73,0xd2,0x85,0x8b,0xa5,0x00,0xe4,0x11,0x25,0x5a,0x24,0xae,0x40,0xf9,
++		0x60,0xe3,0xc6,0x45,0xed,0xd8,0xb6,0x26,0xdd,0x73,0x86,0xf7,0xba,0xbf,0x36,0x2b,
++		0x07,0xe0,0xb0,0xe3,0x13,0x11,0xf0,0x1a,0x91,0x2b,0xe9,0x77,0x09,0x32,0xd9,0xd1,
++		0x4c,0xaa,0x1e,0x45,0x3b,0x4a,0x5b,0xcf,0xc7,0xa4,0x6a,0xd4,0x55,0x95,0x77,0x20, /* 1520 */
++		0x40,0x91,0x04,0x7b,0x5f,0xe7,0xa9,0x87,0x79,0xff,0x24,0xd4,0xf4,0xb2,0x54,0xc2,
++		0x4a,0x2a,0xde,0xac,0x7b,0x6f,0xa2,0x79,0xf3,0xe9,0x74,0xcf,0x65,0x94,0xcb,0x11,
++		0x23,0xa6,0x2c,0x09,0x23,0xa9,0x04,0xae,0x45,0xfc,0xd3,0x98,0x6a,0x4d,0x3a,0xca,
++		0x4c,0x52,0x00,0xd1,0xdc,0xa5,0x41,0x19,0x65,0x66,0x4a,0xcf,0x26,0x1c,0x14,0x3f,
++		0xee,0x51,0x58,0x8d,0xe8,0x30,0x44,0xb7,0x3d,0xf5,0x3e,0xbf,0x06,0xad,0x1e,0x1f, /* 1600 */
++		0x31,0xb1,0x3a,0x15,0xc3,0x54,0x41,0x6a,0x7b,0x33,0xa5,0x94,0xc2,0xe8,0x3d,0x6f,
++		0xe8,0xf3,0xfb,0x6b,0x22,0x78,0x5e,0x91,0xe5,0x79,0xc7,0x22,0xb3,0xee,0x45,0xb3,
++		0x76,0xa3,0x7a,0xdf,0xf7,0xa9,0xee,0x24,0x52,0x24,0xa0,0x11,0x0b,0x99,0x65,0x9e,
++		0x7b,0xbc,0xd3,0xf4,0xb0,0x78,0x33,0x91,0x92,0x7b,0x95,0x6c,0x2e,0x40,0xf4,0xa6,
++		0xff,0xfa,0x1e,0x21,0x77,0x79,0x6b,0xe7,0xbe,0xf0,0xfb,0xd0,0xa2,0x34,0x0f,0x56, /* 1680 */
++		0x52,0x33,0x66,0x75,0x25,0x32,0xfa,0x8b,0x5c,0x1b,0xeb,0xcb,0x2e,0xb7,0x43,0x2d,
++		0x39,0x4b,0x24,0x81,0x47,0x32,0x0d,0x60,0x4a,0xa5,0x40,0xfd,0x95,0xa3,0x39,0x60,
++		0x61,0x04,0x4b,0x04,0x90,0x90,0x2d,0x42,0xc1,0x0d,0x86,0x62,0x91,0x3e,0x9b,0xf6,
++		0x56,0x20,0xd4,0xe3,0xd6,0x35,0x8a,0xf3,0xf7,0x55,0x85,0x56,0x59,0x2e,0xa5,0x8b,
++		0x78,0xad,0x84,0x78,0xa1,0xf5,0x07,0xc6,0xc3,0x7f,0x1d,0x1b,0x0c,0x09,0xe6,0xd7, /* 1760 */
++		0x91,0x65,0x5f,0x2d,0xc1,0xd8,0x14,0xd8,0xd4,0x95,0x97,0x0d,0x64,0x1b,0xca,0x30,
++		0x40,0x7a,0x7a,0xd2,0x8a,0xb1,0x01,0x70,0xaa,0x34,0x89,0x97,0x40,0x98,0x5e,0x25,
++		0x5f,0xd6,0x80,0xc0,0x08,0x4f,0xd5,0xe7,0xce,0xce,0x93,0x52,0x6a,0xa9,0x8e,0x06,
++		0x0b,0xc4,0xdb,0x04,0x0f,0x66,0x36,0x21,0x24,0x19,0x28,0x35,0x3d,0xa2,0x15,0xdd,
++		0xfc,0xbf,0xc1,0x51,0xab,0xbf,0x27,0xa6,0xca,0x39,0x33,0x21,0x8f,0x4f,0x26,0x19, /* 1840 */
++		0x08,0x76,0x3c,0xc8,0xd5,0x3e,0xe4,0x31,0xf2,0xd1,0xc4,0x02,0x6e,0x56,0x0a,0x2a,
++		0x78,0x7f,0x2f,0x48,0xe9,0xd8,0xf2,0xcf,0xa1,0xb8,0xb2,0x78,0xbd,0x0b,0x94,0x48,
++		0x0d,0x71,0xb6,0x41,0x14,0x2f,0x7e,0x30,0xc7,0x04,0xaa,0x4c,0x0b,0x5b,0xe7,0xe1,
++		0xde,0x5a,0x1c,0xf3,0x90,0xfb,0x70,0x45,0x77,0x3b,0x2d,0x8f,0xbc,0x9e,0xac,0xe2,
++		0x85,0x44,0x5d,0x7d,0x62,0x7b,0x8b,0xe1,0x30,0xd8,0x76,0xfc,0xd9,0xf1,0xac,0x29, /* 1920 */
++		0xbe,0x66,0x6e,0xbf,0x7b,0x33,0x51,0xda,0x9f,0xb3,0x8f,0xa4,0x42,0xba,0x01,0xea,
++		0x20,0x6b,0x62,0xbe,0x6a,0x25,0x5e,0xdc,0xb7,0x6e,0x1d,0xef,0xfa,0x69,0x06,0x20,
++		0x07,0x6e,0x2f,0x25,0x34,0x4d,0x41,0xed,0x35,0x23,0x1c,0x73,0x1f,0xd0,0x25,0x4a,
++		0xe6,0x41,0x43,0xe0,0x86,0x36,0x7d,0x7c,0xbe,0xa6,0xbe,0x06,0xd1,0x79,0x67,0x3b,
++		0xc0,0xd5,0x9f,0x39,0xf2,0x51,0xc3,0x01,0x26,0x23,0xb5,0xa2,0x0f,0x90,0x26,0x73, /* 2000 */
++		0xf3,0x08,0xa1,0x56,0x7f,0x64,0x44,0x03,0x85,0x5e,0x26,0xfb,0xcd,0x20,0x63,0x2e,
++		0x2e,0xea,0x4d,0xf3,0x5d,0xb4,0xe8,0x81,0x01,0xa4,0x9b,0x67,0x4a,0xf3,0x50,0xe1,
++		0x84,0x80,0x98,0x0a,0xe8,0x2f,0xa3,0xa4,0x09,0x4c,0x39,0x4a,0x59,0xfa,0x8d,0x01,
++		0xe0,0xdf,0x20,0xb5,0xbf,0x98,0x7e,0x3a,0x0a,0x26,0x3a,0x61,0x18,0xab,0xfa,0xf5,
++		0x57,0x9d,0x4b,0x5a,0x21,0x13,0xfd,0x1d,0x49,0xe2,0x94,0xee,0xc7,0x31,0x58,0x46, /* 2080 */
++		0x75,0x35,0x86,0xa7,0x52,0x8f,0xc6,0x84,0x5c,0x9d,0xca,0xcc,0xbd,0xb1,0x65,0xe4,
++		0xe4,0xfa,0x13,0xad,0xfd,0x3e,0x1c,0xcf,0x3a,0x90,0xc9,0xc4,0x1a,0x74,0x73,0xb8,
++		0xea,0x7b,0xca,0x6a,0x68,0xb1,0x3a,0x9e,0x32,0x05,0x40,0x8e,0x7b,0x22,0x80,0x28,
++		0xf3,0xf8,0xb7,0xed,0x3d,0xbe,0x69,0x01,0xdd,0x5a,0x95,0x46,0xd5,0x20,0xfc,0xca,
++		0x65,0x11,0x8a,0x9b,0x99,0x9e,0x8d,0xeb,0x0d,0xea,0x55,0x56,0xf3,0xb6,0xae,0xaf, /* 2160 */
++		0x95,0xd0,0x74,0x3c,0xff,0x1c,0x80,0xcd,0x0c,0x4e,0x2c,0x3f,0x8d,0xc8,0xda,0x8a,
++		0x85,0xae,0x65,0xaf,0x72,0x3d,0x39,0xf5,0x2b,0xe8,0x38,0xcc,0xe5,0x7e,0x28,0x96,
++		0x36,0x4b,0xf8,0x83,0x7e,0x34,0x0a,0xcc,0x33,0x1e,0xae,0x08,0x92,0x9c,0xd1,0x7d,
++		0x79,0x3e,0x66,0x0d,0x13,0xc9,0x69,0x05,0x64,0xa5,0xdb,0xa3,0x33,0xea,0x72,0x51,
++		0x7c,0xa4,0xce,0x82,0x35,0x0d,0xbd,0xbb,0x24,0x13,0xa9,0x5d,0x3a,0xfe,0x0b,0x0a, /* 2240 */
++		0x35,0x0a,0x16,0xda,0x3c,0xe1,0xf6,0x23,0xd7,0xdb,0x78,0xfb,0x2f,0x85,0x39,0x58,
++		0x3c,0xca,0xbb,0x5f,0x86,0xca,0x81,0xdb,0x57,0xeb,0xc7,0xd0,0x26,0x1e,0x04,0x27,
++		0x04,0x0b,0x8b,0xe5,0x10,0x39,0x44,0x0e,0xf8,0x16,0x55,0x50,0x9d,0x72,0x2b,0x93,
++		0xb2,0xc5,0xfe,0xe6,0x08,0x35,0xee,0x80,0xfa,0x47,0x7d,0xa6,0x0a,0xea,0x6f,0x14,
++		0xe0,0x97,0x71,0x8e,0x32,0xef,0x08,0x0c,0x6f,0xf2,0x99,0x3f,0xf8,0x2e,0x94,0xae, /* 2320 */
++		0x82,0xd4,0x72,0x6f,0x68,0x3d,0x8b,0x10,0x73,0x3c,0x51,0x87,0xb8,0xc7,0x9f,0x7b,
++		0x44,0x2d,0x55,0x9b,0x98,0xbb,0x93,0xbf,0xc7,0x59,0xa5,0xab,0xf1,0x1d,0x82,0x56,
++		0x68,0x46,0xbd,0x15,0x35,0xba,0x4c,0x78,0x27,0xc4,0xab,0x6c,0x07,0x98,0x58,0xed,
++		0x39,0x84,0xb8,0x71,0x64,0xb1,0x6e,0x24,0x3f,0xdf,0xfc,0x82,0xec,0x49,0xd0,0x63,
++		0xee,0xfc,0x3c,0x0c,0xfe,0xd9,0x39,0x0e,0xe6,0x7d,0xaf,0x01,0xd6,0xb0,0xb2,0x91, /* 2400 */
++		0xfd,0x92,0xd5,0x11,0x98,0xdf,0xe3,0x26,0x66,0x8e,0xb8,0x80,0xf4,0xf6,0x22,0xd6,
++		0x1f,0x69,0x64,0x8e,0xce,0x77,0x51,0x9f,0x6b,0x25,0xe2,0xb5,0x25,0xc2,0x5f,0x1e,
++		0x34,0x21,0xdd,0x1f,0xd8,0x47,0x1c,0x93,0x44,0xff,0x57,0xac,0x32,0x4b,0x75,0x13,
++		0x90,0xc8,0x3d,0x45,0x65,0x47,0xa1,0xda,0x88,0x41,0x66,0x4c,0xd4,0xa2,0x78,0x8f,
++		0x7e,0x25,0x4d,0x3f,0x6a,0x5a,0xec,0x07,0x2e,0x5a,0xce,0x13,0x34,0x79,0x0e,0xb0, /* 2480 */
++		0x61,0xd8,0xd2,0x1e,0x6c,0xd1,0xd0,0x9d,0x88,0xcd,0x49,0x48,0xd2,0xfd,0x7e,0x2e,
++		0x6d,0x13,0x3e,0xf1,0x10,0xb5,0x7c,0x65,0x1e,0xb8,0xf0,0xf1,0x5d,0x08,0x74,0xc1,
++		0x89,0x0b,0xca,0x8f,0x9f,0x6d,0xf3,0xf0,0xb4,0x85,0x25,0xd1,0x21,0x9a,0x84,0x8c,
++		0x28,0x5f,0x6f,0xdd,0xec,0x17,0x64,0x2e,0x9d,0x65,0x85,0xc7,0x05,0xb3,0x8a,0x27,
++		0x6a,0x4e,0xf9,0x3e,0x40,0x09,0xc5,0x19,0xb7,0xe8,0x14,0x0a,0xe4,0xb6,0x13,0x30, /* 2560 */
++		0x7c,0x27,0x12,0x63,0x7f,0x87,0x65,0xe2,0x22,0x0f,0x50,0x3c,0x58,0x70,0x9c,0x57,
++		0x25,0x25,0xfa,0xfa,0x51,0xc3,0xed,0xd8,0x07,0x90,0x8e,0xb0,0xdf,0x60,0xb2,0x9d,
++		0xf0,0x41,0xa1,0x92,0x3a,0x59,0x6a,0xee,0x60,0x21,0x97,0xf9,0x38,0xac,0x26,0x0a,
++		0x2d,0x3d,0x36,0x2b,0xba,0x9f,0xac,0xc1,0xd6,0x82,0x16,0xc8,0x02,0xb4,0x2b,0x69,
++		0xc5,0x47,0x06,0xb2,0xd2,0x98,0xff,0xcb,0x50,0x41,0x4d,0xa1,0xf8,0xf2,0x68,0x2e, /* 2640 */
++		0x05,0xab,0x22,0xea,0x47,0x1d,0x48,0xb8,0x4d,0xdd,0x20,0x47,0xdf,0x63,0x05,0x56,
++		0xde,0x86,0x4c,0xa2,0xb8,0x66,0x2f,0xcd,0x2a,0x71,0x96,0x83,0x17,0xfe,0xb9,0x2e,
++		0x31,0x41,0xe8,0x7a,0xfd,0x40,0x69,0x55,0x9e,0x95,0x9d,0x8e,0x42,0x54,0x1c,0x32,
++		0xb4,0x9f,0x07,0xb5,0xd3,0x5b,0x33,0xc6,0xd3,0x7c,0x0e,0x70,0x48,0xae,0x64,0x76,
++		0x1c,0xb1,0xe8,0xc3,0x58,0x2e,0xf7,0x89,0x92,0xbe,0xd3,0x4c,0x85,0x6e,0xf7,0xed, /* 2720 */
++		0xd9,0xee,0x9a,0x04,0x57,0x5c,0xbc,0xea,0x1c,0xf2,0xd1,0x64,0xef,0x7c,0x35,0x24,
++		0x41,0x07,0xa6,0x53,0x5a,0x44,0xcc,0x91,0xbc,0xf9,0xf3,0xe9,0xe2,0x3e,0xcd,0x39,
++		0x46,0x82,0xce,0xad,0xf4,0x87,0x0c,0xeb,0x82,0xf1,0x85,0x7a,0x8b,0xc5,0x50,0x0c,
++		0x3d,0xd4,0xbf,0xf3,0x4f,0x6d,0x28,0xbe,0x7b,0x79,0x6b,0x1d,0x9a,0x97,0x68,0x89,
++		0xd7,0xa0,0x76,0x58,0x2a,0x52,0xf8,0x1d,0x63,0x8f,0x73,0xb7,0x48,0xeb,0x13,0xea, /* 2800 */
++		0xe5,0xb8,0xdc,0xad,0x0a,0x48,0x88,0x69,0xf5,0x89,0xb1,0x85,0x9f,0x3f,0x4f,0xcf,
++		0xf8,0xb0,0x6e,0xdf,0x92,0x0f,0xab,0xf9,0xde,0x5e,0xa1,0x48,0x07,0x3c,0xa8,0x05,
++		0x90,0xf9,0x6b,0xf1,0x23,0xec,0x4b,0xcd,0x9a,0xe1,0x8d,0x05,0x75,0xab,0x97,0x4c,
++		0xd0,0xf2,0x5a,0x19,0xe8,0xf7,0x31,0x52,0x23,0xc8,0x55,0xb3,0xb3,0x4a,0x6b,0x35,
++		0xc6,0x12,0x93,0x98,0x6c,0x9f,0x60,0x08,0x06,0x78,0x8c,0x38,0xad,0x4a,0x0c,0xa1, /* 2880 */
++		0x67,0x5a,0xc6,0x3a,0xcc,0xe0,0x4b,0x9a,0x6c,0x51,0x3b,0x98,0xb0,0x76,0x66,0x7e,
++		0xd1,0x5e,0x73,0x30,0x34,0xe0,0xf9,0x46,0x2c,0x1b,0xc7,0xe8,0x86,0xd9,0x4f,0x87,
++		0x24,0x8e,0xeb,0xf8,0xd4,0x32,0xd6,0xdc,0xaf,0x2a,0x1a,0xcf,0xb7,0x8f,0x60,0x61,
++		0x9f,0x54,0xde,0xe9,0x95,0x97,0x34,0x30,0x00,0xa1,0xc1,0xe5,0xaa,0x1f,0x64,0xdf,
++		0x01,0x6a,0xc3,0xbd,0xf8,0xbc,0x1e,0xed,0x25,0x13,0xbc,0xa8,0x01,0x9a,0x4c,0x98, /* 2960 */
++		0x18,0xb4,0x76,0xc1,0x57,0x8a,0xc7,0xfe,0x12,0xb3,0x0a,0x7e,0x44,0xf2,0x6d,0x9c,
++		0xc7,0xf6,0x16,0xcf,0x53,0xa4,0xba,0xfb,0x2d,0x07,0x52,0x26,0x5f,0x1a,0x55,0x7a,
++		0x56,0x79,0x53,0xe4,0x27,0x7b,0x5c,0xbe,0x27,0xeb,0xc9,0x54,0x29,0xe8,0x2f,0x9b,
++		0xc4,0xe7,0x21,0x50,0x55,0x8f,0x90,0xa6,0xf1,0x92,0x81,0x5e,0xec,0x80,0x66,0x93,
++		0xb2,0x29,0xba,0x76,0x1e,0x04,0x3f,0xe3,0x99,0x07,0xba,0x4f,0x1f,0x08,0x5d,0x20, /* 3040 */
++		0x79,0x6a,0xc1,0x3e,0x57,0x29,0x9e,0xb7,0x99,0xc8,0xce,0x0e,0x6a,0xed,0x31,0x26,
++		0x16,0x84,0xe4,0x1e,0x44,0xbd,0x18,0x81,0x91,0x40,0x03,0x45,0xcd,0x46,0xe2,0xa0,
++		0xc9,0x98,0x9a,0xae,0x7e,0xa6,0x23,0xdd,0xd4,0x6b,0xf4,0x19,0x43,0x56,0xf2,0x84,
++		0x26,0x26,0x16,0x49,0xc0,0x02,0x6c,0xaa,0x37,0x01,0xcd,0x6d,0x88,0x79,0xbf,0x3d,
++		0xf4,0x68,0xfc,0x56,0x74,0x71,0x55,0x82,0x61,0xc3,0x6e,0x12,0x9f,0x8c,0x1c,0xfc, /* 3120 */
++		0xeb,0x51,0xb1,0x1d,0x9a,0xa9,0xc4,0x2b,0xfc,0x24,0x01,0xa5,0x77,0x83,0x80,0x09,
++		0x47,0x28,0x7e,0xee,0x14,0x7f,0xdc,0xf5,0xa2,0x62,0x4b,0x98,0xed,0x31,0xc8,0x7d,
++		0x0d,0x91,0xed,0x81,0x56,0x08,0xb0,0xa3,0x10,0xf4,0x78,0xe4,0x3a,0x94,0x25,0xbf,
++		0xf5,0x29,0x3f,0xba,0x12,0x6d,0xfa,0xde,0xfb,0x59,0xae,0x43,0x42,0xd7,0x32,0xe5,
++		0x3d,0xf7,0x36,0xc1,0x0f,0xa4,0x2f,0x21,0x5d,0x8a,0x6b,0xfe,0x5e,0xe9,0xaa,0x54, /* 3200 */
++		0x7d,0xf7,0x19,0xc4,0xbf,0x13,0x95,0x21,0xda,0x77,0x2c,0x04,0xda,0xfa,0x97,0xbf,
++		0x9a,0x35,0x37,0xf7,0x47,0xf2,0xdd,0x8b,0x86,0xcc,0xa2,0x2a,0x7d,0x73,0xba,0xac,
++		0xea,0x00,0x34,0x8f,0xf3,0x3c,0xd3,0x1a,0xb5,0xd8,0xca,0x8b,0x6e,0x1d,0x73,0x84,
++		0xec,0xaa,0x7a,0x5c,0x0c,0x73,0x0e,0xa5,0x1f,0x40,0x9f,0xaa,0xea,0x9c,0xc2,0xb7,
++		0x4c,0x7b,0x9c,0xe3,0xd6,0x50,0xbc,0x57,0x7e,0x5a,0x2d,0xe8,0xfd,0x1d,0xcd,0x80, /* 3280 */
++		0xf8,0xf4,0x5c,0xb1,0x91,0xca,0x74,0xdf,0x6b,0xa2,0x31,0xff,0x05,0xec,0x47,0xa1,
++		0x9c,0xfa,0x03,0x18,0x33,0xd8,0x14,0x2e,0x65,0xb8,0xbc,0x9d,0x73,0x1c,0xa5,0x67,
++		0x5d,0xcb,0x0c,0x1a,0x88,0xd9,0x1b,0xf7,0xfd,0x12,0x43,0x4f,0x08,0x83,0xd5,0xaa,
++		0x27,0xd7,0x92,0xc6,0x50,0x0c,0x9e,0x87,0xb3,0x60,0x01,0x7a,0xa5,0x28,0x9c,0x32,
++		0xfa,0xff,0x53,0x6c,0x99,0xf2,0x8a,0xd6,0x62,0xf2,0x8e,0xb4,0x73,0x39,0xf2,0x49, /* 3360 */
++		0x90,0xec,0x05,0x9c,0x11,0x76,0xeb,0xb2,0x2f,0x0a,0x90,0x65,0x5a,0x9e,0x81,0x93,
++		0xc1,0x17,0xf2,0x38,0x84,0x6f,0x47,0xc1,0x57,0xca,0x50,0xf2,0x0f,0xbc,0x72,0x71,
++		0xd9,0x12,0xe5,0xea,0x4d,0xfb,0x1d,0x84,0x6f,0x90,0xf6,0x13,0x96,0x1b,0xc3,0xc5,
++		0x54,0xb4,0x4a,0x47,0x55,0xfc,0x23,0x72,0x09,0xee,0x44,0x8b,0xf7,0x7f,0xef,0xcc,
++		0x64,0x4b,0xf8,0xa2,0x23,0x79,0x02,0x9f,0x49,0x4d,0xc0,0x43,0x45,0xe6,0xba,0x3b, /* 3440 */
++		0x77,0x18,0x42,0xb8,0xda,0x20,0x8b,0xd7,0xb6,0xd7,0x01,0x25,0x74,0xff,0xd7,0x17,
++		0x4c,0x88,0x9e,0x14,0x7a,0x3e,0xce,0x40,0x15,0xf9,0x9e,0xe9,0x24,0xaa,0x87,0x76,
++		0x79,0x13,0xd9,0x2d,0xd7,0x79,0x33,0x81,0x54,0x63,0xf1,0xcf,0xc6,0x26,0x47,0x51,
++		0xf7,0x62,0xc4,0x55,0x14,0x7d,0x99,0x16,0xba,0x95,0x75,0x44,0xaa,0x3a,0x08,0x4e,
++		0x19,0x99,0xb0,0xae,0x89,0xf4,0x26,0xf0,0xaa,0x8f,0xc7,0x48,0x8d,0xcb,0x93,0x9a, /* 3520 */
++		0x6e,0xdb,0x74,0x36,0x0e,0x6c,0xee,0x44,0xb5,0xcc,0x82,0x64,0xd0,0x6d,0xf5,0x4d,
++		0x59,0x27,0x18,0x58,0xdf,0xdd,0x7a,0x86,0xc4,0x72,0xb5,0x2b,0xf9,0xc2,0xf3,0xbd,
++		0xdb,0x28,0x15,0xfa,0xb4,0xa6,0x24,0x1a,0xa0,0xa5,0x60,0x46,0xe1,0x8a,0x53,0xdb,
++		0xe5,0xb0,0xd5,0x9a,0x00,0xc7,0x30,0xc3,0xcd,0x8a,0xbe,0xd6,0xc7,0x5b,0x3b,0x5b,
++		0x7c,0x80,0xc7,0x68,0xcc,0xe9,0x0f,0x8d,0xa8,0x6a,0x8e,0xae,0xcd,0xaf,0xb3,0x19, /* 3600 */
++		0xe3,0x72,0xe6,0x2d,0x44,0x6e,0xa8,0xdc,0xb7,0x1c,0x64,0x8f,0xd3,0x45,0x52,0x2a,
++		0xdb,0x1d,0xee,0xc9,0xaf,0x48,0xf9,0x4b,0xa2,0x9f,0x9b,0xf7,0xb2,0xdd,0x3d,0xf7,
++		0x24,0x29,0x6e,0x6c,0x77,0x99,0x8e,0xfe,0x81,0x59,0xfe,0xa5,0x8f,0x9c,0xdd,0xe4,
++		0x29,0x45,0xe6,0xe5,0xb4,0xdb,0x87,0xfb,0xbf,0xdf,0x28,0x26,0x62,0xaf,0x54,0xf1,
++		0xf1,0x4a,0x9d,0x30,0x3c,0x63,0xa2,0xcb,0x79,0x3d,0x1f,0xfe,0x8d,0x25,0xe6,0xbb, /* 3680 */
++		0xc7,0x0b,0x4e,0x7b,0x93,0xda,0x85,0x85,0x15,0x5a,0x6b,0xfd,0xea,0xe9,0x8d,0xf1,
++		0x62,0x06,0xed,0x3e,0xcc,0x5d,0x2b,0xcb,0x54,0x09,0x50,0xc5,0x18,0x5a,0xc9,0x7c,
++		0x51,0x20,0x0b,0xda,0x47,0xd4,0x9d,0x89,0xa7,0xa6,0x80,0x22,0xb3,0x0a,0x54,0xb6,
++		0x0f,0xe4,0x29,0x62,0xcf,0x19,0x4e,0x6f,0x7a,0xcb,0x8f,0x85,0xc9,0xe0,0x9e,0xf7,
++		0x85,0x1a,0x8b,0x61,0xea,0x23,0x62,0xf3,0xd9,0x47,0x86,0x0a,0x33,0x24,0x2d,0xde, /* 3760 */
++		0xae,0xd4,0xce,0x81,0x3f,0xb7,0x52,0x72,0xdb,0x45,0x72,0x42,0xf0,0xf0,0x00,0x9b,
++		0xc5,0x9d,0x4b,0x30,0xfe,0x17,0xa9,0x98,0x72,0x3a,0x0b,0xc6,0x0b,0x3f,0xdc,0x7f,
++		0x5c,0x0b,0x90,0xf4,0xb6,0xb9,0xf2,0x13,0x4e,0x01,0xe0,0xac,0x03,0x59,0x54,0xfd,
++		0xa7,0x3f,0xb2,0x21,0x64,0xe5,0x74,0x82,0x26,0x4c,0x48,0x17,0xde,0xbe,0x6f,0x7a,
++		0xf9,0xc6,0xc5,0x29,0xe5,0xc1,0x9e,0xe5,0x7b,0x97,0xfa,0x79,0x2d,0xfb,0x82,0x7d, /* 3840 */
++		0xd6,0xc4,0x1c,0x2e,0x4a,0x09,0xf0,0xe8,0x64,0x89,0x0f,0x59,0x8f,0x29,0x98,0x28,
++		0xc1,0x9b,0xb9,0x2e,0xde,0x7d,0xba,0x93,0x50,0xfa,0xf6,0xae,0x5c,0x2f,0x71,0xe3,
++		0x8b,0x2c,0xf0,0x8c,0x23,0x3a,0x2f,0x4d,0xbf,0xaa,0x9b,0xd1,0x76,0x2f,0xad,0xfd,
++		0xf6,0x9a,0x60,0x2e,0x07,0xc8,0x99,0x0b,0x06,0x99,0x4d,0x65,0x7f,0xe2,0x1e,0x10,
++		0xc2,0xa7,0x7e,0x75,0xc4,0x9d,0x70,0x6c,0x51,0x40,0xa3,0xd8,0x35,0x6e,0x08,0x5b, /* 3920 */
++		0xc6,0x71,0x2b,0x06,0x91,0x1c,0x73,0x93,0xde,0xd2,0x36,0x2c,0x59,0xc1,0x75,0x2b,
++		0x59,0xf4,0xcf,0xd9,0x77,0x2a,0x9d,0x52,0x41,0xd0,0x12,0xcf,0x85,0x78,0xa4,0x28,
++		0x77,0xe8,0x2f,0x7d,0x20,0x00,0xa0,0x96,0x79,0x8a,0xc6,0x8b,0x09,0xed,0x39,0x89,
++		0x6a,0xc6,0xd7,0x0c,0x9f,0x16,0x63,0xae,0x89,0xeb,0x13,0xd3,0x20,0x59,0x8e,0x66,
++		0xc3,0x88,0xce,0x57,0xc7,0xe3,0x7d,0xef,0x62,0x0d,0xa2,0x81,0xf9,0x55,0xed,0xb5, /* 4000 */
++		0x61,0x39,0x44,0x26,0x56,0x8b,0xbb,0x50,0xf2,0x30,0x57,0x05,0x1d,0xf8,0x62,0xe6,
++		0x2e,0xb8,0x42,0x95,0x24,0x56,0xd3,0x25,0x56,0xd4,0xba,0xd2,0x88,0xa5,0x08,0x6e,
++		0x7e,0xed,0x9a,0x95,0x37,0x23,0xe9,0x26,0x9a,0x28,0xb0,0x1a,0xb0,0x0e,0xfd,0x5b,
++		0xa4,0xf0,0x78,0xb2,0xe0,0x91,0xf9,0x35,0x04,0xcf,0x77,0x99,0xd3,0x51,0x28,0xda,
++		0xd9,0xf5,0xc0,0xb7,0x7a,0x6c,0x34,0x28,0x7d,0x2a,0xa7,0x2e,0xe9,0x43,0x9b,0xc2, /* 4080 */
++		0xcd,0x4d,0xb9,0x8a,0xab,0x21,0xac,0x36,0xf5,0x85,0x07,0x36,0xf3,0x58,0x70,0xd6,
++		0x54,0xf2,0xec,0x20,0x0b,0x19,0x7c,0x99,0x4f,0x65,0x5d,0xd8,0xb7,0x64,0x96,0xb1,
++		0x1a,0x5c,0x45,0x9a,0x39,0x52,0x32,0x53,0x68,0x87,0x3a,0xbf,0xa4,0x4c,0xfa,0x58,
++		0x39,0x13,0xbb,0x9c,0x82,0x49,0xfe,0xd6,0x61,0x18,0xcb,0x75,0x24,0x5b,0x4c,0x0e,
++		0xf8,0x4b,0x71,0x71,0xb0,0xb3,0xb4,0x6c,0x50,0x5d,0x00,0x65,0xda,0x9a,0x3c,0x8c, /* 4160 */
++		0xc8,0x36,0xfe,0x80,0xba,0x30,0xa6,0x27,0x5c,0xbf,0xf7,0x77,0x3b,0xe5,0x09,0x79,
++		0xb8,0xcc,0xd2,0xed,0xcd,0xbb,0xcc,0xd4,0xc5,0xbe,0xf9,0x8b,0xca,0x3c,0x92,0x2e,
++		0x7d,0x95,0xe2,0x1b,0xd1,0x84,0xf2,0xa0,0x35,0x26,0xf9,0xe9,0xef,0xef,0x63,0xa9,
++		0x7b,0x58,0x49,0xe8,0xb2,0x53,0x59,0x4d,0x0d,0xc6,0xee,0x32,0x91,0x04,0x55,0x0b,
++		0xda,0xd7,0x48,0xa4,0x70,0xc9,0xae,0xd2,0xd5,0x80,0x2c,0x1d,0x31,0xb5,0xaa,0x7a, /* 4240 */
++		0xd6,0xd2,0xec,0x85,0x78,0x76,0x88,0x26,0x5b,0x81,0xf4,0x9e,0xde,0x15,0xdc,0xe0,
++		0x47,0x27,0x4d,0x6e,0xdb,0xed,0xa8,0xc5,0x79,0x3b,0xe3,0x4d,0xde,0x69,0x14,0xed,
++		0xc4,0x90,0x07,0x93,0xf6,0xbe,0x9e,0x09,0x4d,0xfe,0x24,0xfa,0xf3,0x31,0x72,0x6b,
++		0x03,0xb6,0x9d,0x70,0xaa,0xde,0x6a,0xbd,0xba,0x65,0x6c,0x4e,0xab,0x44,0x30,0x5e,
++		0xb5,0x2d,0xfe,0x6a,0xc6,0xec,0x8c,0x3d,0x81,0x5a,0xef,0x7e,0x46,0x4e,0x97,0x81, /* 4320 */
++		0x08,0x91,0xeb,0xb0,0x45,0x22,0x30,0x28,0x4f,0x64,0x7c,0x88,0x3d,0xd1,0x35,0x23,
++		0x27,0xc5,0x2e,0x08,0x73,0xf0,0xba,0x88,0x6a,0x02,0x0f,0x65,0x4d,0x62,0xc0,0xf5,
++		0x82,0xba,0x04,0xf1,0x78,0xc7,0xcc,0x4c,0xfe,0xd7,0x0f,0x12,0xf6,0x64,0x50,0xeb,
++		0xf1,0x2f,0x5c,0xdb,0x6f,0xed,0x94,0x29,0x6b,0x7f,0x30,0x6c,0x7f,0xd6,0xdd,0x3b,
++		0x19,0xd1,0x9b,0x09,0x87,0x1e,0xf8,0xfe,0xae,0xa6,0x5f,0x7d,0xa5,0x38,0x19,0x5a, /* 4400 */
++		0x22,0xcc,0x24,0xae,0xbe,0x41,0x8f,0x5e,0xc8,0xf6,0x6a,0x65,0x9e,0xea,0x86,0x85,
++		0xee,0xe9,0xb6,0x45,0x0e,0xe4,0xa0,0x82,0x5c,0x82,0x1e,0xb7,0xd4,0x66,0x6a,0x86,
++		0x29,0x5e,0x34,0x20,0xec,0x73,0x5c,0x1f,0x89,0xe8,0xb3,0x1e,0x6b,0xb7,0xb6,0x48,
++		0x2e,0x69,0x13,0xae,0x3d,0x2c,0x8d,0x43,0x68,0x34,0x93,0xfe,0xa8,0x9b,0x2d,0xd2,
++		0x9e,0x97,0x0d,0x3f,0xfb,0x1b,0x07,0xf9,0x95,0x3f,0x31,0x36,0xbf,0x15,0x1c,0x82, /* 4480 */
++		0xa5,0xc1,0xa5,0x6b,0x6a,0x3b,0x61,0xd4,0xbf,0x05,0xfe,0xcb,0x2e,0x96,0x4b,0x68,
++		0xbf,0x09,0xb5,0x11,0x8b,0x31,0x36,0xb4,0xd3,0xb3,0xd1,0x15,0x52,0xff,0x83,0xf4,
++		0x57,0xd9,0xf5,0x6d,0x90,0x37,0xf2,0xd7,0x93,0xce,0x04,0x67,0x7a,0x6f,0xdf,0x13,
++		0x71,0xa9,0x85,0x7a,0x94,0xab,0x7f,0x9d,0x87,0x24,0xee,0x1f,0x66,0x4b,0x15,0x3b,
++		0x0e,0x11,0x20,0x5f,0x90,0x94,0xfb,0x1e,0x43,0x45,0x4b,0x62,0x68,0xcc,0xd6,0xf1, /* 4560 */
++		0xf7,0xb3,0x23,0x33,0xbf,0xc1,0xcd,0x00,0x0b,0x28,0x44,0x92,0xa0,0xbb,0xeb,0x31,
++		0x4e,0x7f,0x81,0x82,0x93,0x03,0x6f,0xf5,0xf9,0xff,0x04,0x21,0x41,0x77,0xc1,0xf8,
++		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x11,0x12,0x17,0x1f,
++		0x25,0x2a,0x30),                                                                 /* 4627 */
++	}
++};
++
++static char msg_str[] =
++	"Digital signatures are used to detect unauthorized modifications to data "
++	"and to authenticate the identity of the signatory. In addition, the "
++	"recipient of signed data can use a digital signature as evidence in "
++	"demonstrating to a third party that the signature was, in fact, generated "
++	"by the claimed signatory. This is known as non-repudiation since the "
++	"signatory cannot easily repudiate the signature at a later time. This "
++	"standard specifies ML-DSA, a set of algorithms that can be used  to "
++	"generate and verify digital signatures. ML-DSA is believed to be secure, "
++	"even against adversaries in possession of a large-scale quantum computer.";
++
++
++START_TEST(test_ml_dsa_load)
++{
++	private_key_t *key;
++	public_key_t *pubkey, *public;
++	chunk_t encoding, asn1, short_key;
++
++	/* load ASN.1 encoded private key in PKCS#8 format */
++	asn1 = chunk_cat("cc", tests[_i].asn1_seed, tests[_i].seed);
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
++							 BUILD_BLOB_ASN1_DER, asn1, BUILD_END);
++	ck_assert(key != NULL);
++
++	/* check ML-DSA private key type */
++	ck_assert(key->get_type(key) == tests[_i].type);
++
++	/* check ML-DSA private key encoding */
++	ck_assert(key->get_encoding(key, PRIVKEY_ASN1_DER, &encoding));
++	ck_assert_chunk_eq(encoding, asn1);
++	chunk_free(&encoding);
++	key->destroy(key);
++
++	/* loading a private key blob with an incorrect key size must fail */
++	short_key = chunk_skip(tests[_i].seed, 1);
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, tests[_i].type,
++							 BUILD_BLOB, short_key, BUILD_END);
++	ck_assert(key == NULL);
++
++	/* load private key blob */
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, tests[_i].type,
++							 BUILD_BLOB, tests[_i].seed, BUILD_END);
++	ck_assert(key != NULL);
++
++	/* check ML-DSA private key type */
++	ck_assert(key->get_type(key) == tests[_i].type);
++
++	/* check ML-DSA private key encoding */
++	ck_assert(key->get_encoding(key, PRIVKEY_ASN1_DER, &encoding));
++	ck_assert_chunk_eq(encoding, asn1);
++	chunk_free(&encoding);
++	chunk_free(&asn1);
++
++	/* load ASN.1 encoded public key in PKCS#1 format */
++	asn1 = chunk_cat("cc", tests[_i].asn1_pubkey, tests[_i].pubkey);
++	pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
++								BUILD_BLOB_ASN1_DER, asn1, BUILD_END);
++	ck_assert(pubkey != NULL);
++
++	/* check ML-DSA public key type */
++	ck_assert(pubkey->get_type(pubkey) == tests[_i].type);
++
++	/* check ML-DSA public key encoding */
++	ck_assert(pubkey->get_encoding(pubkey, PUBKEY_SPKI_ASN1_DER, &encoding));
++	ck_assert_chunk_eq(encoding, asn1);
++	chunk_free(&encoding);
++	pubkey->destroy(pubkey);
++
++	/* loading a public key blob with an incorrect key size must fail */
++	short_key = chunk_skip(tests[_i].pubkey, 1);
++	pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, tests[_i].type,
++								BUILD_BLOB, short_key, BUILD_END);
++	ck_assert(pubkey == NULL);
++
++	/* load public key blob */
++	pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, tests[_i].type,
++								BUILD_BLOB, tests[_i].pubkey, BUILD_END);
++	ck_assert(pubkey != NULL);
++
++	/* check ML-DSA public key type */
++	ck_assert(pubkey->get_type(pubkey) == tests[_i].type);
++
++	/* check ML-DSA public key encoding */
++	ck_assert(pubkey->get_encoding(pubkey, PUBKEY_SPKI_ASN1_DER, &encoding));
++	ck_assert_chunk_eq(encoding, asn1);
++	chunk_free(&encoding);
++	chunk_free(&asn1);
++
++	/* compare public keys */
++	public = key->get_public_key(key);
++	ck_assert(public != NULL);
++	ck_assert(public->equals(public, pubkey));
++
++	/* cleanup */
++	key->destroy(key);
++	pubkey->destroy(pubkey);
++	public->destroy(public);
++}
++END_TEST
++
++START_TEST(test_ml_dsa_sig)
++{
++	private_key_t *key;
++	signature_scheme_t scheme;
++	pqc_params_t pqc_params;
++	chunk_t msg, signature;
++
++	/* load private key */
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, tests[_i].type,
++							 BUILD_BLOB, tests[_i].seed, BUILD_END);
++	ck_assert(key != NULL);
++
++	/* prepare signature generation */
++	scheme = signature_scheme_from_oid(key_type_to_oid(tests[_i].type));
++	msg = chunk_from_str(msg_str);
++
++	/* set deterministic signatures */
++	pqc_params.deterministic = TRUE;
++
++	/* set non-empty context string */
++	pqc_params.ctx = chunk_from_str("strongSwan Project");
++
++	/* generate and verify signature with non-empty context string */
++	ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
++	if (chunk_equals(signature, tests[_i].sig))
++	{
++		warn("non-empty context string in ML-DSA signature not supported");
++	}
++	else
++	{
++		ck_assert_chunk_eq(signature, tests[_i].sig_ctx);
++	}
++	chunk_free(&signature);
++
++	/* set empty context string */
++	pqc_params.ctx = chunk_empty;
++
++	/* generate and verify signature with empty context string */
++	ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
++	ck_assert_chunk_eq(signature, tests[_i].sig);
++
++	/* cleanup */
++	chunk_free(&signature);
++	key->destroy(key);
++}
++END_TEST
++
++START_TEST(test_ml_dsa_verify)
++{
++	public_key_t *pubkey;
++	signature_scheme_t scheme;
++	pqc_params_t pqc_params;
++	chunk_t msg;
++
++	/* load public key */
++	pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, tests[_i].type,
++								BUILD_BLOB, tests[_i].pubkey, BUILD_END);
++	ck_assert(pubkey != NULL);
++
++	/* check ML-DSA public key type */
++	ck_assert(pubkey->get_type(pubkey) == tests[_i].type);
++
++	/* prepare signature verification */
++	scheme = signature_scheme_from_oid(key_type_to_oid(tests[_i].type));
++	msg = chunk_from_str(msg_str);
++
++	/* set deterministic signatures */
++	pqc_params.deterministic = TRUE;
++
++	/* set non-empty context string */
++	pqc_params.ctx = chunk_from_str("strongSwan Project");
++
++	/* verify signature with non-empty context string */
++	if (pubkey->verify(pubkey, scheme, &pqc_params, msg, tests[_i].sig))
++	{
++		warn("non-empty context string in ML-DSA signature not supported");
++	}
++	else
++	{
++		ck_assert(pubkey->verify(pubkey, scheme, &pqc_params, msg,
++								 tests[_i].sig_ctx));
++	}
++
++	/* set empty context string */
++	pqc_params.ctx = chunk_empty;
++
++	/* verify signature */
++	ck_assert(pubkey->verify(pubkey, scheme, &pqc_params, msg, tests[_i].sig));
++
++	/* cleanup */
++	pubkey->destroy(pubkey);
++}
++END_TEST
++
++START_TEST(test_ml_dsa_gen)
++{
++	private_key_t *key;
++	public_key_t *pubkey;
++	signature_scheme_t scheme;
++	chunk_t msg = chunk_from_str("ML-DSA is the algorithm for the future");
++	key_type_t type;
++	chunk_t sig1 = chunk_empty, sig2 = chunk_empty;
++
++	/* set type */
++	type = tests[_i].type;
++
++	/* generate private key */
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, BUILD_END);
++	ck_assert(key != NULL);
++
++	/* check ML-DSA private key type */
++	ck_assert(key->get_type(key) == type);
++
++	/* generate a randomized signature without context string */
++	scheme = signature_scheme_from_oid(key_type_to_oid(type));
++	ck_assert(key->sign(key, scheme, NULL, msg, &sig1));
++
++	/* generate a second signature which should be distict */
++	ck_assert(key->sign(key, scheme, NULL, msg, &sig2));
++	ck_assert(!chunk_equals(sig1, sig2));
++
++	/* extract public key */
++	pubkey = key->get_public_key(key);
++	ck_assert(pubkey != NULL);
++
++	/* check ML-DSA public key type */
++	ck_assert(pubkey->get_type(pubkey) == type);
++
++	/* verify signatures */
++	ck_assert(pubkey->verify(pubkey, scheme, NULL, msg, sig1));
++	ck_assert(pubkey->verify(pubkey, scheme, NULL, msg, sig2));
++
++	/* cleanup */
++	key->destroy(key);
++	pubkey->destroy(pubkey);
++	chunk_free(&sig1);
++	chunk_free(&sig2);
++}
++END_TEST
++
++START_TEST(test_ml_dsa_sig_fail)
++{
++	public_key_t *pubkey;
++	signature_scheme_t scheme;
++	chunk_t msg, sig;
++	const u_int k = tests[_i].k ;
++	const u_int omega = tests[_i].omega;
++
++	/* load public key */
++	pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, tests[_i].type,
++								BUILD_BLOB, tests[_i].pubkey, BUILD_END);
++	ck_assert(pubkey != NULL);
++
++	scheme = signature_scheme_from_oid(key_type_to_oid(tests[_i].type));
++	msg = chunk_from_str(msg_str);
++
++	/* signature with an incorrect size */
++	sig = chunk_skip(tests[_i].sig, 1);
++	ck_assert(!pubkey->verify(pubkey, scheme, NULL, msg, sig));
++
++	/* signature with a decreasing maximum hint index */
++	sig = chunk_clone(tests[_i].sig);
++	sig.ptr[sig.len - k + 1] = sig.ptr[sig.len - k] - 1;
++	ck_assert(!pubkey->verify(pubkey, scheme, NULL, msg, sig));
++	chunk_free(&sig);
++
++	/* signature with an oversize hint index */
++	sig = chunk_clone(tests[_i].sig);
++	sig.ptr[sig.len - k] = omega + 1;
++	ck_assert(!pubkey->verify(pubkey, scheme, NULL, msg, sig));
++	chunk_free(&sig);
++
++	/* signature with a non-encreasing hint positions */
++	sig = chunk_clone(tests[_i].sig);
++	sig.ptr[sig.len - omega - k + 1] = sig.ptr[sig.len - omega - k] - 1;
++	ck_assert(!pubkey->verify(pubkey, scheme, NULL, msg, sig));
++	chunk_free(&sig);
++
++	/* signature with first non-used hint bit not set to zero */
++	sig = chunk_clone(tests[_i].sig);
++	sig.ptr[sig.len - omega - k + sig.ptr[sig.len - 1]] = 0xff;
++	ck_assert(!pubkey->verify(pubkey, scheme, NULL, msg, sig));
++	chunk_free(&sig);
++
++	/* signature with last non-used hint bit not set to zero */
++	sig = chunk_clone(tests[_i].sig);
++	sig.ptr[sig.len - k - 1] = 0xff;
++	ck_assert(!pubkey->verify(pubkey, scheme, NULL, msg, sig));
++	chunk_free(&sig);
++
++	/* cleanup */
++	pubkey->destroy(pubkey);
++}
++END_TEST
++
++Suite *ml_dsa_suite_create()
++{
++	Suite *s;
++	TCase *tc;
++
++	s = suite_create("ml_dsa");
++
++	tc = tcase_create("ml_dsa_load");
++	tcase_add_loop_test(tc, test_ml_dsa_load, 0, countof(tests));
++	suite_add_tcase(s, tc);
++
++	tc = tcase_create("ml_dsa_sig");
++	tcase_add_loop_test(tc, test_ml_dsa_sig, 0, countof(tests));
++	suite_add_tcase(s, tc);
++
++	tc = tcase_create("ml_dsa_verify");
++	tcase_add_loop_test(tc, test_ml_dsa_verify, 0, countof(tests));
++	suite_add_tcase(s, tc);
++
++	tc = tcase_create("ml_dsa_gen");
++	tcase_add_loop_test(tc, test_ml_dsa_gen, 0, countof(tests));
++	suite_add_tcase(s, tc);
++
++	tc = tcase_create("ml_dsa_sig_fail");
++	tcase_add_loop_test(tc, test_ml_dsa_sig_fail, 0, countof(tests));
++	suite_add_tcase(s, tc);
++	return s;
++}
+diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h
+index bcd8dffd2c9..f6d8f05fa38 100644
+--- a/src/libstrongswan/tests/tests.h
++++ b/src/libstrongswan/tests/tests.h
+@@ -63,6 +63,7 @@ TEST_SUITE_DEPEND(prf_plus_suite_create, KDF, KDF_PRF_PLUS)
+ TEST_SUITE_DEPEND(fetch_http_suite_create, FETCHER, "http://")
+ TEST_SUITE_DEPEND(ed25519_suite_create, PRIVKEY_GEN, KEY_ED25519)
+ TEST_SUITE_DEPEND(ed448_suite_create, PRIVKEY_GEN, KEY_ED448)
++TEST_SUITE_DEPEND(ml_dsa_suite_create, PRIVKEY_GEN, KEY_ML_DSA_44)
+ TEST_SUITE(signature_params_suite_create)
+ TEST_SUITE(metadata_suite_create)
+ TEST_SUITE(metadata_set_suite_create)
+
+diff --git a/src/libstrongswan/plugins/botan/Makefile.am b/src/libstrongswan/plugins/botan/Makefile.am
+index 20826c3a3ff..32feff863d4 100644
+--- a/src/libstrongswan/plugins/botan/Makefile.am
++++ b/src/libstrongswan/plugins/botan/Makefile.am
+@@ -27,6 +27,8 @@ libstrongswan_botan_la_SOURCES = \
+ 	botan_ec_private_key.h botan_ec_private_key.c \
+ 	botan_ed_public_key.h botan_ed_public_key.c \
+ 	botan_ed_private_key.h botan_ed_private_key.c \
++	botan_ml_dsa_public_key.h botan_ml_dsa_public_key.c \
++	botan_ml_dsa_private_key.h botan_ml_dsa_private_key.c \
+ 	botan_util.h botan_util.c \
+ 	botan_util_keys.h botan_util_keys.c \
+ 	botan_aead.h botan_aead.c \
+diff --git a/src/libstrongswan/plugins/botan/botan_ml_dsa_private_key.c b/src/libstrongswan/plugins/botan/botan_ml_dsa_private_key.c
+new file mode 100644
+index 00000000000..2aefde0144b
+--- /dev/null
++++ b/src/libstrongswan/plugins/botan/botan_ml_dsa_private_key.c
+@@ -0,0 +1,309 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "botan_ml_dsa_private_key.h"
++#include "botan_ml_dsa_public_key.h"
++#include "botan_util.h"
++
++#include <botan/build.h>
++
++#ifdef BOTAN_HAS_ML_DSA
++
++#include <asn1/asn1.h>
++#include <utils/debug.h>
++#include <credentials/keys/signature_params.h>
++
++typedef struct private_private_key_t private_private_key_t;
++
++#define ML_DSA_SEED_LEN 32
++
++/**
++ * Private data
++ */
++struct private_private_key_t {
++
++	/**
++	 * Public interface
++	 */
++	private_key_t public;
++
++	/**
++	 * Botan private key object
++	 */
++	botan_privkey_t key;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Reference count
++	 */
++	refcount_t ref;
++};
++
++/* from botan_ml_dsa_public_key.c */
++const char *botan_ml_dsa_get_mldsa_mode(key_type_t type);
++
++METHOD(private_key_t, sign, bool,
++	private_private_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t *signature)
++{
++	pqc_params_t pqc_params;
++	char *sig_mode;
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		return FALSE;
++	}
++	sig_mode = pqc_params.deterministic ? "Deterministic" : "Randomized";
++	pqc_params_free(&pqc_params);
++
++	switch (scheme)
++	{
++		case SIGN_ML_DSA_44:
++		case SIGN_ML_DSA_65:
++		case SIGN_ML_DSA_87:
++			return botan_get_signature(this->key, sig_mode, data, signature);
++		default:
++			DBG1(DBG_LIB, "signature scheme %N not supported via botan",
++				 signature_scheme_names, scheme);
++			return FALSE;
++	}
++}
++
++METHOD(private_key_t, decrypt, bool,
++	private_private_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "ML-DSA private key decryption not implemented");
++	return FALSE;
++}
++
++METHOD(private_key_t, get_keysize, int,
++	private_private_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++METHOD(private_key_t, get_type, key_type_t,
++	private_private_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(private_key_t, get_public_key, public_key_t*,
++	private_private_key_t *this)
++{
++	botan_pubkey_t pubkey;
++
++	if (botan_privkey_export_pubkey(&pubkey, this->key))
++	{
++		return NULL;
++	}
++	return botan_ml_dsa_public_key_adopt(pubkey, this->type);
++}
++
++METHOD(private_key_t, get_fingerprint, bool,
++	private_private_key_t *this, cred_encoding_type_t type,	chunk_t *fingerprint)
++{
++	botan_pubkey_t pubkey;
++	bool success = FALSE;
++
++	/* check the cache before doing the export */
++	if (lib->encoding->get_cache(lib->encoding, type, this, fingerprint))
++	{
++		return TRUE;
++	}
++
++	if (botan_privkey_export_pubkey(&pubkey, this->key))
++	{
++		return FALSE;
++	}
++	success = botan_get_fingerprint(pubkey, this, type, fingerprint);
++	botan_pubkey_destroy(pubkey);
++
++	return success;
++}
++
++METHOD(private_key_t, get_encoding, bool,
++	private_private_key_t *this, cred_encoding_type_t type,
++	chunk_t *encoding)
++{
++	return botan_get_privkey_encoding(this->key, type, encoding);
++}
++
++METHOD(private_key_t, get_ref, private_key_t*,
++	private_private_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(private_key_t, destroy, void,
++	private_private_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this);
++		botan_privkey_destroy(this->key);
++		free(this);
++	}
++}
++
++/**
++ * Internal generic constructor
++ */
++static private_private_key_t *create_empty(key_type_t type)
++{
++	private_private_key_t *this;
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.sign = _sign,
++			.decrypt = _decrypt,
++			.get_keysize = _get_keysize,
++			.get_public_key = _get_public_key,
++			.equals = private_key_equals,
++			.belongs_to = private_key_belongs_to,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = private_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.ref = 1,
++	);
++
++	return this;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *botan_ml_dsa_private_key_adopt(botan_privkey_t key,
++											  key_type_t type)
++{
++	private_private_key_t *this;
++
++	this = create_empty(type);
++	this->key = key;
++
++	return &this->public;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *botan_ml_dsa_private_key_gen(key_type_t type, va_list args)
++{
++	private_private_key_t *this;
++	botan_rng_t rng;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_KEY_SIZE:
++				/* just ignore the key size */
++				va_arg(args, u_int);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (!botan_get_rng(&rng, RNG_TRUE))
++	{
++		return NULL;
++	}
++
++	this = create_empty(type);
++
++	if (botan_privkey_create(&this->key, "ML-DSA",
++			botan_ml_dsa_get_mldsa_mode(type), rng))
++	{
++		DBG1(DBG_LIB, "%N private key generation failed", key_type_names, type);
++		botan_rng_destroy(rng);
++		free(this);
++		return NULL;
++	}
++
++	botan_rng_destroy(rng);
++	return &this->public;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *botan_ml_dsa_private_key_load(key_type_t type, va_list args)
++{
++	private_private_key_t *this;
++	chunk_t priv = chunk_empty;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				priv = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (priv.len == ML_DSA_SEED_LEN + 2 &&
++		priv.ptr[0] == 0x80 && priv.ptr[1] == ML_DSA_SEED_LEN)
++	{
++		priv = chunk_skip(priv, 2);
++	}
++	if (priv.len != ML_DSA_SEED_LEN)
++	{
++		DBG1(DBG_LIB, "error: the size of the loaded ML-DSA private key seed "
++			 "is %u bytes instead of %d bytes", priv.len, ML_DSA_SEED_LEN);
++		return NULL;
++	}
++
++	this = create_empty(type);
++
++	if (botan_privkey_load_ml_dsa(&this->key, priv.ptr, priv.len,
++								  botan_ml_dsa_get_mldsa_mode(type)))
++	{
++		destroy(this);
++		return NULL;
++	}
++
++	return &this->public;
++}
++
++#endif
+diff --git a/src/libstrongswan/plugins/botan/botan_ml_dsa_private_key.h b/src/libstrongswan/plugins/botan/botan_ml_dsa_private_key.h
+new file mode 100644
+index 00000000000..df3ccddc9d1
+--- /dev/null
++++ b/src/libstrongswan/plugins/botan/botan_ml_dsa_private_key.h
+@@ -0,0 +1,63 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++/**
++ * @defgroup botan_ml_dsa_private_key botan_ml_dsa_private_key
++ * @{ @ingroup botan_p
++ */
++
++#ifndef BOTAN_ML_DSA_PRIVATE_KEY_H_
++#define BOTAN_ML_DSA_PRIVATE_KEY_H_
++
++#include <botan/ffi.h>
++
++#include <credentials/builder.h>
++#include <credentials/keys/private_key.h>
++
++/**
++ * Generate an ML-DSA private key using Botan.
++ *
++ * @param type		key type must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			generated key, NULL on failure
++ */
++private_key_t *botan_ml_dsa_private_key_gen(key_type_t type, va_list args);
++
++/**
++ * Load an ML-DSA private key using Botan.
++ *
++ * @param type		key type must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++private_key_t *botan_ml_dsa_private_key_load(key_type_t type, va_list args);
++
++/**
++ * Load an ML-DSA private key by adopting a botan_privkey_t object.
++ *
++ * @param key		private key object (adopted)
++ * @param type		key type must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @return			loaded key, NULL on failure
++ */
++private_key_t *botan_ml_dsa_private_key_adopt(botan_privkey_t key,
++											  key_type_t type);
++#endif /** BOTAN_ML_DSA_PRIVATE_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/botan/botan_ml_dsa_public_key.c b/src/libstrongswan/plugins/botan/botan_ml_dsa_public_key.c
+new file mode 100644
+index 00000000000..7deebc554df
+--- /dev/null
++++ b/src/libstrongswan/plugins/botan/botan_ml_dsa_public_key.c
+@@ -0,0 +1,222 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "botan_ml_dsa_public_key.h"
++#include "botan_util.h"
++
++#include <botan/build.h>
++
++#ifdef BOTAN_HAS_ML_DSA
++
++#include <utils/debug.h>
++
++typedef struct private_public_key_t private_public_key_t;
++
++/**
++ * Private data
++ */
++struct private_public_key_t {
++
++	/**
++	 * Public interface
++	 */
++	public_key_t public;
++
++	/**
++	 * Botan public key object
++	 */
++	botan_pubkey_t key;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Reference counter
++	 */
++	refcount_t ref;
++};
++
++METHOD(public_key_t, get_type, key_type_t,
++	private_public_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(public_key_t, get_keysize, int,
++	private_public_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++METHOD(public_key_t, verify, bool,
++	private_public_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t signature)
++{
++	switch (scheme)
++	{
++		case SIGN_ML_DSA_44:
++		case SIGN_ML_DSA_65:
++		case SIGN_ML_DSA_87:
++			return botan_verify_signature(this->key, "Pure", data, signature);
++		default:
++			DBG1(DBG_LIB, "signature scheme %N not supported via botan",
++				 signature_scheme_names, scheme);
++			return FALSE;
++	}
++}
++
++METHOD(public_key_t, encrypt, bool,
++	private_public_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "ML-DSA public key encryption not implemented");
++	return FALSE;
++}
++
++METHOD(public_key_t, get_fingerprint, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint)
++{
++	return botan_get_fingerprint(this->key, this, type, fingerprint);
++}
++
++METHOD(public_key_t, get_encoding, bool,
++	private_public_key_t *this, cred_encoding_type_t type,
++	chunk_t *encoding)
++{
++	return botan_get_encoding(this->key, type, encoding);
++}
++
++METHOD(public_key_t, get_ref, public_key_t*,
++	private_public_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(public_key_t, destroy, void,
++	private_public_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this);
++		botan_pubkey_destroy(this->key);
++		free(this);
++	}
++}
++
++/**
++ * Internal generic constructor
++ */
++static private_public_key_t *create_empty(key_type_t type)
++{
++	private_public_key_t *this;
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.verify = _verify,
++			.encrypt = _encrypt,
++			.get_keysize = _get_keysize,
++			.equals = public_key_equals,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = public_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.ref = 1,
++	);
++
++	return this;
++}
++
++/*
++ * Described in header
++ */
++public_key_t *botan_ml_dsa_public_key_adopt(botan_pubkey_t key,
++											key_type_t type)
++{
++	private_public_key_t *this;
++
++	this = create_empty(type);
++	this->key = key;
++
++	return &this->public;
++}
++
++/**
++ * Returns the Botan ML-DSA mode for a given key type
++ */
++ const char *botan_ml_dsa_get_mldsa_mode(key_type_t type)
++ {
++	switch (type)
++	{
++		case KEY_ML_DSA_44:
++			return "ML-DSA-4x4";
++		case KEY_ML_DSA_65:
++			return "ML-DSA-6x5";
++		case KEY_ML_DSA_87:
++			return "ML-DSA-8x7";
++		default:
++			return NULL;
++	}
++}
++
++/*
++ * Described in header
++ */
++public_key_t *botan_ml_dsa_public_key_load(key_type_t type, va_list args)
++{
++	private_public_key_t *this;
++	chunk_t key = chunk_empty;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				key = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	this = create_empty(type);
++
++	if (botan_pubkey_load_ml_dsa(&this->key, key.ptr, key.len,
++								 botan_ml_dsa_get_mldsa_mode(type)))
++	{
++		free(this);
++		return NULL;
++	}
++
++	return &this->public;
++}
++
++#endif
+diff --git a/src/libstrongswan/plugins/botan/botan_ml_dsa_public_key.h b/src/libstrongswan/plugins/botan/botan_ml_dsa_public_key.h
+new file mode 100644
+index 00000000000..63ce0dc57d4
+--- /dev/null
++++ b/src/libstrongswan/plugins/botan/botan_ml_dsa_public_key.h
+@@ -0,0 +1,55 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++/**
++ * @defgroup botan_ml_dsa_public_key botan_ml_dsa_public_key
++ * @{ @ingroup botan_p
++ */
++
++#ifndef BOTAN_ML_DSA_PUBLIC_KEY_H_
++#define BOTAN_ML_DSA_PUBLIC_KEY_H_
++
++#include <botan/ffi.h>
++
++#include <credentials/builder.h>
++#include <credentials/keys/public_key.h>
++
++/**
++ * Load an ML-DSA public key by adopting a botan_pubkey_t object.
++ *
++ * @param key		public key object (adopted)
++ * @param type		key type must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @return			loaded key, NULL on failure
++ */
++public_key_t *botan_ml_dsa_public_key_adopt(botan_pubkey_t key,
++											key_type_t type);
++
++/**
++ * Load an ML-DSA public key using Botan.
++ *
++ * @param type		key type must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++public_key_t *botan_ml_dsa_public_key_load(key_type_t type, va_list args);
++
++#endif /** BOTAN_ML_DSA_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/botan/botan_plugin.c b/src/libstrongswan/plugins/botan/botan_plugin.c
+index 876a0754e30..7d9d2e01832 100644
+--- a/src/libstrongswan/plugins/botan/botan_plugin.c
++++ b/src/libstrongswan/plugins/botan/botan_plugin.c
+@@ -40,6 +40,8 @@
+ #include "botan_ec_private_key.h"
+ #include "botan_ed_public_key.h"
+ #include "botan_ed_private_key.h"
++#include "botan_ml_dsa_public_key.h"
++#include "botan_ml_dsa_private_key.h"
+ #include "botan_aead.h"
+ #include "botan_util_keys.h"
+ #include "botan_x25519.h"
+@@ -220,7 +222,7 @@ METHOD(plugin_t, get_features, int,
+ 
+ 		/* generic key loaders */
+ #if defined (BOTAN_HAS_RSA) || defined(BOTAN_HAS_ECDSA) || \
+-	defined(BOTAN_HAS_ED25519)
++	defined(BOTAN_HAS_ED25519) || defined(BOTAN_HAS_ML_DSA)
+ 		PLUGIN_REGISTER(PUBKEY, botan_public_key_load, TRUE),
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
+ #ifdef BOTAN_HAS_RSA
+@@ -231,6 +233,11 @@ METHOD(plugin_t, get_features, int,
+ #endif
+ #ifdef BOTAN_HAS_ED25519
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_ED25519),
++#endif
++#ifdef BOTAN_HAS_ML_DSA
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
+ #endif
+ 		PLUGIN_REGISTER(PRIVKEY, botan_private_key_load, TRUE),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
+@@ -243,6 +250,11 @@ METHOD(plugin_t, get_features, int,
+ #ifdef BOTAN_HAS_ED25519
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ED25519),
+ #endif
++#ifdef BOTAN_HAS_ML_DSA
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++#endif
+ #endif
+ 		/* RSA */
+ #ifdef BOTAN_HAS_RSA
+@@ -359,6 +371,28 @@ METHOD(plugin_t, get_features, int,
+ 			PLUGIN_PROVIDE(HASHER, HASH_IDENTITY),
+ #endif
+ 
++#ifdef BOTAN_HAS_ML_DSA
++		/* ML-DSA private/public key loading */
++		PLUGIN_REGISTER(PUBKEY, botan_ml_dsa_public_key_load, TRUE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
++		PLUGIN_REGISTER(PRIVKEY, botan_ml_dsa_private_key_load, TRUE),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++		PLUGIN_REGISTER(PRIVKEY_GEN, botan_ml_dsa_private_key_gen, FALSE),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_87),
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_44),
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_65),
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_87),
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_44),
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_65),
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_87),
++#endif
++
+ #ifdef BOTAN_HAS_ML_KEM
+ 		PLUGIN_REGISTER(KE, botan_kem_create),
+ 			PLUGIN_PROVIDE(KE, ML_KEM_512),
+diff --git a/src/libstrongswan/plugins/botan/botan_util.c b/src/libstrongswan/plugins/botan/botan_util.c
+index 0c867037a0c..e218f221a6a 100644
+--- a/src/libstrongswan/plugins/botan/botan_util.c
++++ b/src/libstrongswan/plugins/botan/botan_util.c
+@@ -177,6 +177,10 @@ bool botan_get_encoding(botan_pubkey_t pubkey, cred_encoding_type_t type,
+ 		{
+ 			part = CRED_PART_EDDSA_PUB_ASN1_DER;
+ 		}
++		else if (streq(algo, "ML-DSA"))
++		{
++			part = CRED_PART_PUB_ASN1_DER;
++		}
+ 		success = lib->encoding->encode(lib->encoding, type, NULL, encoding,
+ 										part, asn1_encoding, CRED_PART_END);
+ 	}
+diff --git a/src/libstrongswan/plugins/botan/botan_util_keys.c b/src/libstrongswan/plugins/botan/botan_util_keys.c
+index 127d988cb21..a63bb25adeb 100644
+--- a/src/libstrongswan/plugins/botan/botan_util_keys.c
++++ b/src/libstrongswan/plugins/botan/botan_util_keys.c
+@@ -26,6 +26,8 @@
+ #include "botan_ec_private_key.h"
+ #include "botan_ed_public_key.h"
+ #include "botan_ed_private_key.h"
++#include "botan_ml_dsa_public_key.h"
++#include "botan_ml_dsa_private_key.h"
+ #include "botan_rsa_public_key.h"
+ #include "botan_rsa_private_key.h"
+ 
+@@ -128,6 +130,21 @@ public_key_t *botan_public_key_load(key_type_t type, va_list args)
+ 		this = botan_ed_public_key_adopt(pubkey);
+ 	}
+ 	else
++#endif
++#ifdef BOTAN_HAS_ML_DSA
++	if (streq(name, "ML-DSA") && (type == KEY_ANY || type == KEY_ML_DSA_44 ||
++								  type == KEY_ML_DSA_65 || type == KEY_ML_DSA_87))
++	{
++		if (type == KEY_ANY)
++		{
++			type = public_key_info_decode(blob, NULL);
++		}
++		if (type != KEY_ANY)
++		{
++			this = botan_ml_dsa_public_key_adopt(pubkey, type);
++		}
++	}
++	else
+ #endif
+ 	{
+ 		botan_pubkey_destroy(pubkey);
+@@ -158,6 +175,25 @@ static int determine_ec_oid(chunk_t pkcs8)
+ }
+ #endif
+ 
++#ifdef BOTAN_HAS_ML_DSA
++/**
++ * Determine the ML-DSA private key type from a PKCS#8 structure
++ */
++static key_type_t key_type_from_pkcs8(chunk_t pkcs8)
++{
++	int oid = OID_UNKNOWN;
++	chunk_t inner;
++
++	if (asn1_unwrap(&pkcs8, &pkcs8) == ASN1_SEQUENCE &&
++		asn1_unwrap(&pkcs8, &inner) == ASN1_INTEGER &&
++		asn1_parse_integer_uint64(inner) == 0)
++	{
++		oid = asn1_parse_algorithmIdentifier(pkcs8, 0, NULL);
++	}
++	return key_type_from_oid(oid);
++}
++#endif
++
+ /*
+  * Described in header
+  */
+@@ -232,6 +268,21 @@ private_key_t *botan_private_key_load(key_type_t type, va_list args)
+ 	{
+ 		this = botan_ed_private_key_adopt(key);
+ 	}
++	else
++#endif
++#ifdef BOTAN_HAS_ML_DSA
++	if (streq(name, "ML-DSA") && (type == KEY_ANY || type == KEY_ML_DSA_44 ||
++								  type == KEY_ML_DSA_65 || type == KEY_ML_DSA_87))
++	{
++		if (type == KEY_ANY)
++		{
++			type = key_type_from_pkcs8(blob);
++		}
++		if (type != KEY_ANY)
++		{
++			this = botan_ml_dsa_private_key_adopt(key, type);
++		}
++	}
+ #endif
+ 
+ 	if (!this)
+
+diff --git a/src/libstrongswan/plugins/ml/ml_bitpacker.c b/src/libstrongswan/plugins/ml/ml_bitpacker.c
+index d208bbb510d..9170ae1ddec 100644
+--- a/src/libstrongswan/plugins/ml/ml_bitpacker.c
++++ b/src/libstrongswan/plugins/ml/ml_bitpacker.c
+@@ -98,6 +98,10 @@ METHOD(ml_bitpacker_t, write_bits, bool,
+ 		flush_buffer(this);
+ 		this->bits_buf = 0;
+ 		this->bits_left = 32;
++		if (bits == 0)
++		{
++			return TRUE;
++		}
+ 	}
+ }
+ 
+
+diff --git a/configure.ac b/configure.ac
+index a64c3c58bcd..fbb67a4bece 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1597,7 +1597,7 @@ ADD_PLUGIN([kdf],                  [s charon swanctl pki scripts nm cmd])
+ ADD_PLUGIN([ctr],                  [s charon scripts nm cmd])
+ ADD_PLUGIN([ccm],                  [s charon scripts nm cmd])
+ ADD_PLUGIN([gcm],                  [s charon scripts nm cmd])
+-ADD_PLUGIN([ml],                   [s charon scripts nm cmd])
++ADD_PLUGIN([ml],                   [s charon swanctl pki scripts nm cmd])
+ ADD_PLUGIN([drbg],                 [s charon swanctl pki scripts nm cmd])
+ ADD_PLUGIN([curl],                 [s charon pki scripts nm cmd])
+ ADD_PLUGIN([files],                [s charon pki scripts nm cmd])
+diff --git a/src/libstrongswan/plugins/ml/Makefile.am b/src/libstrongswan/plugins/ml/Makefile.am
+index 6cd57704201..ab9381d2580 100644
+--- a/src/libstrongswan/plugins/ml/Makefile.am
++++ b/src/libstrongswan/plugins/ml/Makefile.am
+@@ -13,9 +13,13 @@ endif
+ libstrongswan_ml_la_SOURCES = \
+ 	ml_bitpacker.c ml_bitpacker.h \
+ 	ml_kem.c ml_kem.h \
+-	ml_params.c ml_params.h \
++	ml_dsa_public_key.c ml_dsa_public_key.h \
++	ml_dsa_private_key.c ml_dsa_private_key.h \
++	ml_kem_params.c ml_kem_params.h \
++	ml_dsa_params.c ml_dsa_params.h \
+ 	ml_plugin.h ml_plugin.c \
+ 	ml_poly.c ml_poly.h \
++	ml_dsa_poly.c ml_dsa_poly.h \
+ 	ml_utils.c ml_utils.h
+ 
+ libstrongswan_ml_la_LDFLAGS = -module -avoid-version
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_params.c b/src/libstrongswan/plugins/ml/ml_dsa_params.c
+new file mode 100644
+index 00000000000..2ced4e9ed79
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_params.c
+@@ -0,0 +1,89 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "ml_dsa_params.h"
++
++/**
++ * Parameter sets for ML-DSA.
++ */
++static const ml_dsa_params_t ml_dsa_params[] = {
++	{
++		.type = KEY_ML_DSA_44,
++		.k = 4,
++		.l = 4,
++		.eta = 2,
++		.d = 3,
++		.gamma1_exp = 17,
++		.gamma2 = (ML_DSA_Q - 1) / 88,
++		.gamma2_d = 6,
++		.lambda = 128,
++		.tau = 39,
++		.beta = 78,
++		.omega = 80,
++		.privkey_len = 2560,
++		.sig_len = 2420,
++	},
++	{
++		.type = KEY_ML_DSA_65,
++		.k = 6,
++		.l = 5,
++		.eta = 4,
++		.d = 4,
++		.gamma1_exp = 19,
++		.gamma2_d = 4,
++		.gamma2 = (ML_DSA_Q - 1) / 32,
++		.lambda = 192,
++		.tau = 49,
++		.beta = 196,
++		.omega = 55,
++		.privkey_len = 4032,
++		.sig_len = 3309,
++	},
++	{
++		.type = KEY_ML_DSA_87,
++		.k = 8,
++		.l = 7,
++		.eta = 2,
++		.d = 3,
++		.gamma1_exp = 19,
++		.gamma2 = (ML_DSA_Q - 1) / 32,
++		.gamma2_d = 4,
++		.lambda = 256,
++		.tau = 60,
++		.beta = 120,
++		.omega = 75,
++		.privkey_len = 4896,
++		.sig_len = 4627,
++
++	},
++};
++
++/*
++ * Described in header
++ */
++const ml_dsa_params_t *ml_dsa_params_get(key_type_t type)
++{
++	int i;
++
++	for (i = 0; i < countof(ml_dsa_params); i++)
++	{
++		if (ml_dsa_params[i].type == type)
++		{
++			return &ml_dsa_params[i];
++		}
++	}
++	return NULL;
++}
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_params.h b/src/libstrongswan/plugins/ml/ml_dsa_params.h
+new file mode 100644
+index 00000000000..a23d143aabb
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_params.h
+@@ -0,0 +1,173 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup ml_dsa_params ml_dsa_params
++ * @{ @ingroup ml_p
++ */
++
++#ifndef ML_PARAMS_H_
++#define ML_PARAMS_H_
++
++#include <credentials/keys/public_key.h>
++
++typedef struct ml_dsa_params_t ml_dsa_params_t;
++
++/**
++ * Constant N used throughout the algorithms.
++ */
++#define ML_DSA_N  256
++
++/**
++ * The prime q = 2^23 - 2^13 + 1.
++ */
++#define ML_DSA_Q  8380417
++
++/**
++ * Number of bits representing (q - 1).
++ */
++#define ML_DSA_Q_BITS  23
++
++/**
++ * The inverse of q mod 2^32.
++ */
++#define ML_DSA_QINV  58728449
++
++/**
++ * Dropped bits from vector t -> (t0, t1)
++ */
++#define ML_DSA_D  13
++
++/**
++ * Number of bits representing element of vector t1.
++ */
++#define ML_DSA_T1_BITS  ML_DSA_Q_BITS - ML_DSA_D
++
++/**
++ * Length of the secret seed, rho, and K
++ */
++#define ML_DSA_SEED_LEN  32
++
++/**
++ * Length of K.
++ */
++#define ML_DSA_K_LEN  32
++
++/**
++ * Length of the public key digest tr.
++ */
++#define ML_DSA_TR_LEN  64
++
++/**
++ * Length of the message representative mu.
++ */
++#define ML_DSA_MU_LEN  64
++
++/**
++ * Length of rnd used for a randomized signature.
++ */
++#define ML_DSA_RND_LEN  32
++
++/**
++ * Length of the random private seed rho_pp.
++ */
++#define ML_DSA_RHO_PP_LEN  64
++
++/**
++ * Parameters for ML-DSA.
++ */
++struct ml_dsa_params_t {
++
++	/**
++	 * Key type.
++	 */
++	const key_type_t type;
++
++	/**
++	 * Number of lines in matrix A.
++	 */
++	uint8_t k;
++
++	/**
++	 * Number of columns in matrix A.
++	 */
++	uint8_t l;
++
++	/**
++	 * Private key range.
++	 */
++	uint8_t eta;
++
++	/**
++	 * Number of bits of a compressesd s1/s2 polynomial coefficient.
++	 */
++	uint8_t d;
++
++	/**
++	 * Power of two exponent of gamma1.
++	 */
++	u_int gamma1_exp;
++
++	/**
++	 * Low-order rounding range.
++	 */
++	int32_t gamma2;
++
++	/**
++	 * Number of bits of a compressed w1 polynomial coefficient.
++	 */
++	size_t gamma2_d;
++
++	/**
++	 * Collision strength of c_tilde.
++	 */
++	size_t lambda;
++
++	/**
++	 * Hamming weight.
++	 */
++	int32_t tau;
++
++	/**
++	 * beta = eta * tau.
++	 */
++	int32_t beta;
++
++	/**
++	 * Maximum number of 1's in the hint h.
++	 */
++	int32_t omega;
++
++	/**
++	 * Private key length in bytes
++	 */
++	size_t privkey_len;
++
++	/**
++	 * Signature length in bytes
++	 */
++	size_t sig_len;
++};
++
++/**
++ * Get parameters from a specific ML-DSA algorithm.
++ *
++ * @param 				type of key
++ * @return				parameters, NULL if not supported
++ */
++const ml_dsa_params_t *ml_dsa_params_get(key_type_t type);
++
++#endif /** ML_PARAMS_H_ @}*/
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_poly.c b/src/libstrongswan/plugins/ml/ml_dsa_poly.c
+new file mode 100644
+index 00000000000..7524aea3804
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_poly.c
+@@ -0,0 +1,406 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "ml_dsa_poly.h"
++#include "ml_utils.h"
++
++/**
++ * Precalculated Zeta^BitRev_8(i) mod q values for NTT Algorithms 41 and 42
++ * The values are in (centered) Montgomery form, not the verbatim values of
++ * Appendix B in FIPS 204.
++ */
++static const int32_t ml_dsa_zetas[ML_DSA_N] = {
++         0,    25847, -2608894,  -518909,   237124,  -777960,  -876248,   466468,
++   1826347,  2353451,  -359251, -2091905,  3119733, -2884855,  3111497,  2680103,
++   2725464,  1024112, -1079900,  3585928,  -549488, -1119584,  2619752, -2108549,
++  -2118186, -3859737, -1399561, -3277672,  1757237,   -19422,  4010497,   280005,
++   2706023,    95776,  3077325,  3530437, -1661693, -3592148, -2537516,  3915439,
++  -3861115, -3043716,  3574422, -2867647,  3539968,  -300467,  2348700,  -539299,
++  -1699267, -1643818,  3505694, -3821735,  3507263, -2140649, -1600420,  3699596,
++    811944,   531354,   954230,  3881043,  3900724, -2556880,  2071892, -2797779,
++  -3930395, -1528703, -3677745, -3041255, -1452451,  3475950,  2176455, -1585221,
++  -1257611,  1939314, -4083598, -1000202, -3190144, -3157330, -3632928,   126922,
++   3412210,  -983419,  2147896,  2715295, -2967645, -3693493,  -411027, -2477047,
++   -671102, -1228525,   -22981, -1308169,  -381987,  1349076,  1852771, -1430430,
++  -3343383,   264944,   508951,  3097992,    44288, -1100098,   904516,  3958618,
++  -3724342,    -8578,  1653064, -3249728,  2389356,  -210977,   759969, -1316856,
++    189548, -3553272,  3159746, -1851402, -2409325,  -177440,  1315589,  1341330,
++   1285669, -1584928,  -812732, -1439742, -3019102, -3881060, -3628969,  3839961,
++   2091667,  3407706,  2316500,  3817976, -3342478,  2244091, -2446433, -3562462,
++    266997,  2434439, -1235728,  3513181, -3520352, -3759364, -1197226, -3193378,
++    900702,  1859098,   909542,   819034,   495491, -1613174,   -43260,  -522500,
++   -655327, -3122442,  2031748,  3207046, -3556995,  -525098,  -768622, -3595838,
++    342297,   286988, -2437823,  4108315,  3437287, -3342277,  1735879,   203044,
++   2842341,  2691481, -2590150,  1265009,  4055324,  1247620,  2486353,  1595974,
++  -3767016,  1250494,  2635921, -3548272, -2994039,  1869119,  1903435, -1050970,
++  -1333058,  1237275, -3318210, -1430225,  -451100,  1312455,  3306115, -1962642,
++  -1279661,  1917081, -2546312, -1374803,  1500165,   777191,  2235880,  3406031,
++   -542412, -2831860, -1671176, -1846953, -2584293, -3724270,   594136, -3776993,
++  -2013608,  2432395,  2454455,  -164721,  1957272,  3369112,   185531, -1207385,
++  -3183426,   162844,  1616392,  3014001,   810149,  1652634, -3694233, -1799107,
++  -3038916,  3523897,  3866901,   269760,  2213111,  -975884,  1717735,   472078,
++   -426683,  1723600, -1803090,  1910376, -1667432, -1104333,  -260646, -3833893,
++  -2939036, -2235985,  -420899, -2286327,   183443,  -976891,  1612842, -3545687,
++   -554416,  3919660,   -48306, -1362209,  3937738,  1400424,  -846154,  1976782
++};
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_ntt(ml_dsa_poly_t *a)
++{
++	u_int len, start, j, k = 0;
++	int32_t zeta, t;
++
++	for (len = ML_DSA_N/2; len > 0; len >>= 1)
++	{
++		for (start = 0; start < ML_DSA_N; start = j + len)
++		{
++			zeta = ml_dsa_zetas[++k];
++			for(j = start; j < start + len; ++j)
++			{
++				t = ml_montgomery_reduce((int64_t)zeta * a->f[j + len]);
++				a->f[j + len] = a->f[j] - t;
++				a->f[j] = a->f[j] + t;
++			}
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_inv_ntt(ml_dsa_poly_t *a)
++{
++	u_int start, len, j, k = ML_DSA_N;
++	int32_t t, zeta;
++
++	/* scaling factor 256^-1 mod q with squared Montgomery multiplier to
++	 * implicitly convert results to Montgomery form (i.e. 2^64/256 mod q)
++	 */
++	const int32_t factor = 41978;
++
++	for (len = 1; len < ML_DSA_N; len <<= 1)
++	{
++		for (start = 0; start < ML_DSA_N; start = j + len)
++		{
++			zeta = -ml_dsa_zetas[--k];
++			for (j = start; j < start + len; ++j)
++			{
++				t = a->f[j];
++				a->f[j] = t + a->f[j + len];
++				a->f[j + len] = t - a->f[j + len];
++				a->f[j + len] = ml_montgomery_reduce((int64_t)zeta * a->f[j + len]);
++			}
++		}
++	}
++
++	for(j = 0; j < ML_DSA_N; ++j)
++	{
++		a->f[j] = ml_montgomery_reduce((int64_t)factor * a->f[j]);
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_ntt_vec(u_int k, ml_dsa_poly_t *a)
++{
++	while (k--)
++	{
++		ml_dsa_poly_ntt(&a[k]);
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_inv_ntt_vec(u_int k, ml_dsa_poly_t *a)
++{
++	while (k--)
++	{
++		ml_dsa_poly_inv_ntt(&a[k]);
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_copy_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b)
++{
++	while (k--)
++	{
++		b[k] = a[k];
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_add_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						 ml_dsa_poly_t *res)
++{
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			res[k].f[n] = a[k].f[n] + b[k].f[n];
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_sub_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						 ml_dsa_poly_t *res)
++{
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			res[k].f[n] = a[k].f[n] - b[k].f[n];
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_mult_const_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++								ml_dsa_poly_t *res)
++{
++	u_int n;
++
++	/* pointwise product of polynomial vector b with a polynomial a */
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			res[k].f[n] = ml_montgomery_reduce((int64_t)a->f[n] * b[k].f[n]);
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_mult_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						  ml_dsa_poly_t *res)
++{
++	u_int n;
++
++	/* initialize result polynomial to all zeros */
++	for (n = 0; n < ML_DSA_N; n++)
++	{
++		res->f[n] = 0;
++	}
++
++	/* compute the inner product of vectors a and b */
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			res->f[n] += ml_montgomery_reduce((int64_t)a[k].f[n] * b[k].f[n]);
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_mult_mat(u_int k, u_int l, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						  ml_dsa_poly_t *res)
++{
++	u_int i;
++
++	for (i = 0; i < k; i++)
++	{
++		ml_dsa_poly_mult_vec(l, &a[i*l], b, &res[i]);
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_reduce_vec(u_int k, ml_dsa_poly_t *a)
++{
++	int32_t r;
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			r = (a[k].f[n] + (1 << 22)) >> 23;
++			a[k].f[n] -= r * ML_DSA_Q;
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_cond_add_q_vec(u_int k, ml_dsa_poly_t *a)
++{
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			a[k].f[n] += (a[k].f[n] >> 31) & ML_DSA_Q;
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_power2round_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *a0,
++								 ml_dsa_poly_t *a1)
++{
++	int32_t t0, t1;
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			t1 = (a[k].f[n] + (1 << (ML_DSA_D-1)) - 1) >> ML_DSA_D;
++			t0 =  a[k].f[n] - (t1 << ML_DSA_D);
++
++			a0[k].f[n] = t0;
++			a1[k].f[n] = t1;
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_shift_left_vec(u_int k, ml_dsa_poly_t *a)
++{
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			a[k].f[n] <<= ML_DSA_D;
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_decompose_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *a0,
++							   ml_dsa_poly_t *a1, int32_t gamma2)
++{
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			ml_decompose(a[k].f[n], &a0[k].f[n], &a1[k].f[n], gamma2);
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++void ml_dsa_poly_use_hint_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *h,
++							  ml_dsa_poly_t *a1, int32_t gamma2)
++{
++	u_int n;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			a1[k].f[n] = ml_use_hint(a[k].f[n], h[k].f[n], gamma2);
++		}
++	}
++}
++
++/*
++ * Described in header
++ */
++u_int ml_dsa_poly_make_hint_vec(u_int k, ml_dsa_poly_t *a0, ml_dsa_poly_t *a1,
++								ml_dsa_poly_t *h, int32_t gamma2)
++{
++	u_int n, s = 0;
++
++	while (k--)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++	  		h[k].f[n] = ml_make_hint(a0[k].f[n], a1[k].f[n], gamma2);
++	  		s += h[k].f[n];
++		}
++	}
++
++	return s;
++}
++
++/*
++ * Described in header
++ */
++bool ml_dsa_poly_check_bound(ml_dsa_poly_t *a, int32_t bound)
++{
++	int32_t t;
++	u_int n;
++
++  /* it is ok to leak which coefficient violates the bound since the probability
++   * for each coefficient is independent of secret data but we must not leak the
++   * sign of the centralized representative.
++   */
++	for (n = 0; n < ML_DSA_N; n++)
++	{
++		t = a->f[n] >> 31;
++		t = a->f[n] - (t & 2*a->f[n]);
++
++		if (t >= bound)
++		{
++			return FALSE;
++		}
++	}
++
++	return TRUE;
++}
++
++/*
++ * Described in header
++ */
++bool ml_dsa_poly_check_bound_vec(u_int k, ml_dsa_poly_t *a, int32_t bound)
++{
++	while (k--)
++	{
++		if (!ml_dsa_poly_check_bound(&a[k], bound))
++		{
++			return FALSE;
++		}
++	}
++
++	return TRUE;
++}
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_poly.h b/src/libstrongswan/plugins/ml/ml_dsa_poly.h
+new file mode 100644
+index 00000000000..06aa8c58e8a
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_poly.h
+@@ -0,0 +1,240 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup ml_dsa_poly ml_dsa_poly
++ * @{ @ingroup ml_p
++ */
++
++#ifndef ML_DSA_POLY_H_
++#define ML_DSA_POLY_H_
++
++#include "ml_dsa_params.h"
++
++typedef struct ml_dsa_poly_t ml_dsa_poly_t;
++
++/**
++ * Represents an element in R_q = Z_q[X]/(X^n + 1) i.e. a polynomial of the
++ * form f[0] + f[1]*X + ... + f[n-1]*X^n-1.
++ */
++struct ml_dsa_poly_t {
++
++	/**
++	 * Coefficients of the polynomial.
++	 */
++	int32_t f[ML_DSA_N];
++};
++
++/**
++ * Computes the NTT.
++ *
++ * Algorithm 41 in FIPS 204.
++ *
++ * @param a		polynomial a (in-place NTT computation)
++ */
++void ml_dsa_poly_ntt(ml_dsa_poly_t *a);
++
++/**
++ * Computes the inverse NTT including scaling.
++ *
++ * Algorithm 42 in FIPS 204.
++ *
++ * @param a		polynomial a (in-place NTT computation)
++ */
++void ml_dsa_poly_inv_ntt(ml_dsa_poly_t *a);
++
++/**
++ * Computes the NTT of each vector element.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a (in-place NTT computation)
++ */
++void ml_dsa_poly_ntt_vec(u_int k, ml_dsa_poly_t *a);
++
++/**
++ * Computes the inverse NTT of each vector element.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a (in-place NTT computation)
++ */
++void ml_dsa_poly_inv_ntt_vec(u_int k, ml_dsa_poly_t *a);
++
++/*
++ * Copy a polynomial vector.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ * @param b		vector of polynomials b
++ */
++void ml_dsa_poly_copy_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b);
++
++/**
++ * Add polynomials in vector a and b (a[i] + b[i] mod q for i in 0 to k-1).
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ * @param b		vector of polynomials b
++ * @param res	vector of resulting polynomials (can be one of the others)
++ */
++void ml_dsa_poly_add_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						 ml_dsa_poly_t *res);
++
++/**
++ * Subtract polynomials in vector a and b (a[i] - b[i] mod q for i in 0 to k-1).
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ * @param b		vector of polynomials b
++ * @param res	vector of resulting polynomials (can be one of the others)
++ */
++void ml_dsa_poly_sub_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						 ml_dsa_poly_t *res);
++
++/**
++ * Pointwise product of a polynomial vector b with a polynomial a.
++ *
++ * @param k		vector size
++ * @param a		polynomial a
++ * @param b		vector of polynomials b
++ * @param res	result vector of polynomials
++ */
++void ml_dsa_poly_mult_const_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++								ml_dsa_poly_t *res);
++
++/**
++ * Dot product of two polynomial vectors a and b.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ * @param b		vector of polynomials b
++ * @param res	result polynomial
++ */
++void ml_dsa_poly_mult_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						  ml_dsa_poly_t *res);
++
++/**
++ * Dot product of a matrix a with a vector b.
++ *
++ * @param k		number of lines in matrix a and size of vector res
++ * @param l		number of columns in matrix a and size of vector b
++ * @param a		kxl matrix a
++ * @param b		vector of polynomials b
++ * @param res	result vector of polynomials
++ */
++void ml_dsa_poly_mult_mat(u_int k, u_int l, ml_dsa_poly_t *a, ml_dsa_poly_t *b,
++						  ml_dsa_poly_t *res);
++
++/**
++ * Computes r = a mod q such that -6283008 <= r <= 6283008 ((2^31-2^22-1) mod q).
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ */
++void ml_dsa_poly_reduce_vec(u_int k, ml_dsa_poly_t *a);
++
++/**
++ * Add q if coefficient is negative.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ */
++void ml_dsa_poly_cond_add_q_vec(u_int k, ml_dsa_poly_t *a);
++
++/**
++ * Decomposes a into (a1, a0) such that a ≡ a1 * 2^d + a0 mod q.
++ *
++ * Algorithm 35 of FIPS 204.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ * @param a0	vector of polynomials a0
++ * @param a1	vector of polynomials a1
++ */
++void ml_dsa_poly_power2round_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *a0,
++								 ml_dsa_poly_t *a1);
++
++/**
++ * Multiply polynomial by 2^d.
++ *
++ * @param k		vector size
++ * @param a		vector of polynomials a
++ */
++void ml_dsa_poly_shift_left_vec(u_int k, ml_dsa_poly_t *a);
++
++/**
++ * Decomposes a into (a1, a0) such that a ≡ a1 * (2 * gamma2) + a0 mod q.
++ *
++ * Algorithm 36 of FIPS 204.
++ *
++ * @param k			vector size
++ * @param a			vector of polynomials a
++ * @param a0		vector of polynomials a0
++ * @param a1		vector of polynomials a1
++ * @param gamma2	parameter gamma2
++*/
++void ml_dsa_poly_decompose_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *a0,
++							   ml_dsa_poly_t *a1, int32_t gamma2);
++
++/**
++ * Return the high bits a1 of a adjusted according to hint h.
++ *
++ * Algorithm 40 of FIPS 204.
++ *
++ * @param k			vector size
++ * @param a			vector of polynomials a
++ * @param h			vector of polynomials h
++ * @param a1		vector of polynomials a1
++ * @param gamma2	parameter gamma2
++*/
++void ml_dsa_poly_use_hint_vec(u_int k, ml_dsa_poly_t *a, ml_dsa_poly_t *h,
++							  ml_dsa_poly_t *a1, int32_t gamma2);
++
++/**
++ * Compute a hint bit indicating whether the low bits a0 of the
++ * input element overflow into the high bits a1.
++ *
++ * Algorithm 39 in FIPS 204.
++ *
++ * @param k			vector size
++ * @param a0		vector of polynomials containg low bits
++ * @param a1		vector of polynomials containing high bits
++ * @param h			vector of polynomials containing hint bits
++ * @param gamma2	parameter gamma2
++ * @return			total numer of hint bits
++ */
++u_int ml_dsa_poly_make_hint_vec(u_int k, ml_dsa_poly_t *a0, ml_dsa_poly_t *a1,
++								ml_dsa_poly_t *h, int32_t gamma2);
++
++/**
++ * Check infinity norm of polynomial against given bound.
++ *
++ * @param a		polynomial a
++ * @param bound	norm bound
++ * @return		TRUE if bound is not exceeded
++ */
++bool ml_dsa_poly_check_bound(ml_dsa_poly_t *a, int32_t bound);
++
++/**
++ * Check infinity norm of vector of polynomials against given bound.
++ *
++ * @param k		vector size
++ * @param a		vector if polynomials a
++ * @param bound	norm bound
++ * @return		TRUE if bound is not exceeded
++ */
++bool ml_dsa_poly_check_bound_vec(u_int k, ml_dsa_poly_t *a, int32_t bound);
++
++#endif /** ML_DSA_POLY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+new file mode 100644
+index 00000000000..359ce4ec326
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+@@ -0,0 +1,1025 @@
++/*
++ * Copyright (C) 2024-2025 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "ml_dsa_private_key.h"
++#include "ml_dsa_params.h"
++#include "ml_dsa_poly.h"
++#include "ml_utils.h"
++#include "ml_bitpacker.h"
++
++#include <library.h>
++#include <utils/debug.h>
++#include <asn1/asn1.h>
++#include <credentials/cred_encoding.h>
++#include <credentials/keys/public_key.h>
++#include <credentials/keys/signature_params.h>
++
++typedef struct private_private_key_t private_private_key_t;
++
++/**
++ * Private data
++ */
++struct private_private_key_t {
++
++	/**
++	 * Public interface
++	 */
++	private_key_t public;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Parameter set.
++	 */
++	const ml_dsa_params_t *params;
++
++	/**
++	 * Secret key seed
++	 */
++	chunk_t keyseed;
++
++	/**
++	 * Public key
++	 */
++	chunk_t pubkey;
++
++	/**
++	 * Private key
++	 */
++	chunk_t privkey;
++
++	/**
++	 * SHAKE-128 instance.
++	 */
++	xof_t *G;
++
++	/**
++	 * SHAKE-256 instance.
++	 */
++	xof_t *H;
++
++	/**
++	 * Reference count
++	 */
++	refcount_t ref;
++};
++
++/* from ml_dsa_public_key.c */
++bool ml_dsa_expand_a(const ml_dsa_params_t *params, xof_t *G, chunk_t rho,
++						ml_dsa_poly_t *a);
++
++bool ml_dsa_sample_in_ball(xof_t *H, int32_t tau, chunk_t rho, ml_dsa_poly_t *c);
++
++bool ml_dsa_w1_encode(u_int k, ml_dsa_poly_t *w1, chunk_t w1_enc, u_int d);
++
++bool ml_dsa_fingerprint(chunk_t pubkey, key_type_t type,
++						cred_encoding_type_t enc_type, chunk_t *fp);
++
++bool ml_dsa_type_supported(key_type_t type);
++
++/**
++ * Decode the secret key (skDecode)
++ *
++ * Algorithm 25 in FIPS 204.
++ */
++static bool decode_secret_key(private_private_key_t *this, chunk_t rho,
++							  chunk_t K, chunk_t tr, ml_dsa_poly_t *s1,
++							  ml_dsa_poly_t *s2, ml_dsa_poly_t *t0)
++{
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	const u_int d = this->params->d;
++	const u_int eta = this->params->eta;
++	u_int i, j, n;
++	uint32_t value;
++	ml_bitpacker_t *bitpacker;
++	chunk_t sk;
++	bool success = FALSE;
++
++	sk = this->privkey;
++	memcpy(rho.ptr, sk.ptr, rho.len);
++	sk = chunk_skip(sk, rho.len);
++
++	memcpy(K.ptr, sk.ptr, K.len);
++	sk = chunk_skip(sk, K.len);
++
++	memcpy(tr.ptr, sk.ptr, tr.len);
++	sk = chunk_skip(sk, tr.len);
++
++	/* unpack the vectors s1, s2 and t0 from the private key blob */
++	bitpacker = ml_bitpacker_create_from_data(sk);
++	for (j = 0; j < l; j++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->read_bits(bitpacker, &value, d) || value > 2*eta)
++			{
++				goto end;
++			}
++			s1[j].f[n] = eta - (int32_t)value;
++		}
++	}
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->read_bits(bitpacker, &value, d) || value > 2*eta)
++			{
++				goto end;
++			}
++			s2[i].f[n] = eta - (int32_t)value;
++		}
++	}
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->read_bits(bitpacker, &value, ML_DSA_D))
++			{
++				goto end;
++			}
++			t0[i].f[n] = (1 << (ML_DSA_D-1)) - (int32_t)value;
++		}
++	}
++	success = TRUE;
++
++end:
++	bitpacker->destroy(bitpacker);
++
++	return success;
++}
++
++/**
++ * Samples a vector y such that each polynomial has coefficients between
++ * -gamma1 + 1 and gamma1.
++ *
++ * Algorithm 34 in FIPS 204.
++ */
++static bool expand_mask(private_private_key_t *this, chunk_t rho, u_int nonce,
++						ml_dsa_poly_t *y)
++{
++	const u_int gamma1_exp = this->params->gamma1_exp;
++	const u_int l = this->params->l;
++	ml_bitpacker_t *bitpacker;
++	chunk_t v;
++	uint32_t value;
++	u_int j, n;
++
++	v = chunk_alloca(32*(1 + gamma1_exp));
++
++	for (j = 0; j < l; j++)
++	{
++		rho.ptr[ML_DSA_RHO_PP_LEN]   = nonce & 0x00ff;
++		rho.ptr[ML_DSA_RHO_PP_LEN+1] = nonce++ >> 8;
++
++		if (!this->H->set_seed(this->H, rho) ||
++			!this->H->get_bytes(this->H, v.len, v.ptr))
++		{
++			return FALSE;
++		}
++
++		bitpacker = ml_bitpacker_create_from_data(v);
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->read_bits(bitpacker, &value, 1 + gamma1_exp))
++			{
++				bitpacker->destroy(bitpacker);
++				return FALSE;
++			}
++			y[j].f[n] = (1 << gamma1_exp) - (int32_t)value;
++		}
++		bitpacker->destroy(bitpacker);
++	}
++
++	return TRUE;
++}
++
++/**
++ * Encodes a signature into a byte string (sigEncode).
++ *
++ * Algorithm 26 in FIPS 204.
++ */
++static chunk_t encode_signature(private_private_key_t *this, chunk_t c_tilde,
++								ml_dsa_poly_t *z, ml_dsa_poly_t *h)
++{
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	const u_int gamma1_exp = this->params->gamma1_exp;
++	const u_int omega = this->params->omega;
++	ml_bitpacker_t *bitpacker;
++	chunk_t signature, sig;
++	u_int i, j, n, index = 0;
++
++	signature = chunk_alloc(this->params->sig_len);
++	sig = signature;
++
++	/* encode byte string c_tilde */
++	memcpy(sig.ptr, c_tilde.ptr, c_tilde.len);
++	sig = chunk_skip(sig, c_tilde.len);
++
++	/* encode vector of polynomials z in packed format */
++	bitpacker = ml_bitpacker_create(sig);
++	for (j = 0; j < l; j++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->write_bits(bitpacker, (1 << gamma1_exp) - z[j].f[n],
++												   1 +  gamma1_exp))
++			{
++				bitpacker->destroy(bitpacker);
++				chunk_free(&signature);
++				return chunk_empty;
++			}
++		}
++	}
++	bitpacker->destroy(bitpacker);
++	sig = chunk_skip(sig, 32 * (1 + gamma1_exp) * l);
++
++	/* encode vector of polynomials with binary coefficients h (HintBitPack)
++	 * Algorithm 20 in FIPS 204.
++	 */
++	memset(sig.ptr, 0x00, omega);
++
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (h[i].f[n] != 0)
++			{
++				sig.ptr[index++] = (uint8_t)n;
++			}
++		}
++		sig.ptr[omega + i] = (uint8_t)index;
++	}
++
++	return signature;
++}
++
++METHOD(private_key_t, sign, bool,
++	private_private_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t *signature)
++{
++
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	const u_int gamma2 = this->params->gamma2;
++	const u_int omega = this->params->omega;
++	const int32_t bound1 = (1 << this->params->gamma1_exp) - this->params->beta;
++	const int32_t bound2 = gamma2 - this->params->beta;
++	pqc_params_t pqc_params;
++	ml_dsa_poly_t a[k*l], s1[l], s2[k], t0[k];
++	ml_dsa_poly_t y[l], z[l], w0[k], w1[k], h[k], c;
++	chunk_t rho, K , tr, seed, mu, rnd, rho_pp, w1_enc, c_tilde;
++	u_int kappa = 0;
++	rng_t *rng;
++
++	/* set empty signature in case of failure */
++	*signature = chunk_empty;
++
++	if (key_type_from_signature_scheme(scheme) != this->type)
++	{
++		DBG1(DBG_LIB, "signature scheme %N not supported",
++					   signature_scheme_names, scheme);
++		return FALSE;
++	}
++
++	rho     = chunk_alloca(ML_DSA_SEED_LEN);
++	K       = chunk_alloca(ML_DSA_K_LEN);
++	tr      = chunk_alloca(ML_DSA_TR_LEN);
++	rnd     = chunk_alloca(ML_DSA_RND_LEN);
++	mu      = chunk_alloca(ML_DSA_MU_LEN);
++	rho_pp  = chunk_alloca(ML_DSA_RHO_PP_LEN + 2);
++	w1_enc  = chunk_alloca(32 * this->params->gamma2_d * k);
++	c_tilde = chunk_alloca(this->params->lambda / 4);
++
++	if (!decode_secret_key(this, rho, K, tr, s1, s2, t0) ||
++		!ml_dsa_expand_a(this->params, this->G, rho, a))
++	{
++		goto cleanup;
++	}
++
++	ml_dsa_poly_ntt_vec(l, s1);
++	ml_dsa_poly_ntt_vec(k, s2);
++	ml_dsa_poly_ntt_vec(k, t0);
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		goto cleanup;
++	}
++
++	/* compute message representative mu */
++	seed = chunk_cat("cmc", tr, pqc_params.pre_ctx, data);
++	if (!this->H->set_seed(this->H, seed) ||
++		!this->H->get_bytes(this->H, ML_DSA_MU_LEN, mu.ptr))
++	{
++		chunk_free(&seed);
++		goto cleanup;
++	}
++	chunk_free(&seed);
++
++
++	/* deterministic or randomized signature? */
++	if 	(pqc_params.deterministic)
++	{
++		memset(rnd.ptr, 0x00, ML_DSA_RND_LEN);
++	}
++	else
++	{
++		rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
++		if (!rng || !rng->get_bytes(rng, ML_DSA_RND_LEN, rnd.ptr))
++		{
++			DESTROY_IF(rng);
++			goto cleanup;
++		}
++		rng->destroy(rng);
++	}
++
++	/* compute random private seed rho_pp */
++	seed = chunk_cat("ccc", K, rnd, mu);
++	if (!this->H->set_seed(this->H, seed) ||
++		!this->H->get_bytes(this->H, ML_DSA_RHO_PP_LEN, rho_pp.ptr))
++	{
++		chunk_clear(&seed);
++		goto cleanup;
++	}
++	chunk_clear(&seed);
++
++	while (TRUE)
++	{
++		if (!expand_mask(this, rho_pp, kappa, y))
++		{
++			goto cleanup;
++		}
++		kappa += l;
++
++		/* multiply vector y with matrix a via NTT resulting in vector w */
++		ml_dsa_poly_copy_vec(l, y, z);
++		ml_dsa_poly_ntt_vec(l, z);
++		ml_dsa_poly_mult_mat(k, l, a, z, w1);
++		ml_dsa_poly_reduce_vec(k, w1);
++		ml_dsa_poly_inv_ntt_vec(k, w1);
++		ml_dsa_poly_cond_add_q_vec(k, w1);
++
++		/* decompose elements of vector w into high and low bits */
++		ml_dsa_poly_decompose_vec(k, w1, w0, w1, gamma2);
++
++		/* compress the w1 vector into a byte string */
++		if (!ml_dsa_w1_encode(k, w1, w1_enc, this->params->gamma2_d))
++		{
++			goto cleanup;
++		}
++
++		/* compute commitment hash c_tilde */
++		seed = chunk_cat("cc", mu, w1_enc);
++		if (!this->H->set_seed(this->H, seed) ||
++			!this->H->get_bytes(this->H, this->params->lambda/4, c_tilde.ptr))
++		{
++			chunk_clear(&seed);
++			goto cleanup;
++		}
++		chunk_clear(&seed);
++
++		/* verifier's challenge */
++		if (!ml_dsa_sample_in_ball(this->H, this->params->tau, c_tilde, &c))
++		{
++			goto cleanup;
++		}
++		ml_dsa_poly_ntt(&c);
++
++		/* compute z, reject if it reveals secret */
++		ml_dsa_poly_mult_const_vec(l, &c, s1, z);
++		ml_dsa_poly_inv_ntt_vec(l, z);
++		ml_dsa_poly_add_vec(l, z, y, z);
++		ml_dsa_poly_reduce_vec(l, z);
++
++		if (!ml_dsa_poly_check_bound_vec(l, z, bound1))
++		{
++			continue;
++		}
++
++		/* check that subtracting cs2 does not change high bits of w and
++		 * low bits do not reveal secret information
++		 */
++		ml_dsa_poly_mult_const_vec(k, &c, s2, h);
++		ml_dsa_poly_inv_ntt_vec(k, h);
++		ml_dsa_poly_sub_vec(k, w0, h, w0);
++		ml_dsa_poly_reduce_vec(k, w0);
++
++		if (!ml_dsa_poly_check_bound_vec(k, w0, bound2))
++		{
++			continue;
++		}
++
++		/* compute hints for w1 */
++		ml_dsa_poly_mult_const_vec(k, &c, t0, h);
++		ml_dsa_poly_inv_ntt_vec(k, h);
++		ml_dsa_poly_reduce_vec(k, h);
++
++		if (!ml_dsa_poly_check_bound_vec(k, h, gamma2))
++		{
++			continue;
++		}
++
++		ml_dsa_poly_add_vec(k, w0, h, w0);
++
++		if (ml_dsa_poly_make_hint_vec(k, w0, w1, h, gamma2) > omega)
++		{
++			continue;
++		}
++
++		/* all checks passed - exit the loop */
++		break;
++	}
++
++	*signature = encode_signature(this, c_tilde, z, h);
++
++cleanup:
++	memwipe(a, sizeof(a));
++	memwipe(s1, sizeof(s1));
++	memwipe(s2, sizeof(s2));
++	memwipe(t0, sizeof(t0));
++	memwipe(K.ptr, K.len);
++
++	return signature->len > 0;
++}
++
++METHOD(private_key_t, decrypt, bool,
++	private_private_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "ML-DSA private key decryption not implemented");
++	return FALSE;
++}
++
++METHOD(private_key_t, get_keysize, int,
++	private_private_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++METHOD(private_key_t, get_type, key_type_t,
++	private_private_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(private_key_t, get_public_key, public_key_t*,
++	private_private_key_t *this)
++{
++	return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, this->type,
++							  BUILD_BLOB, this->pubkey, BUILD_END);
++}
++
++METHOD(private_key_t, get_fingerprint, bool,
++	private_private_key_t *this, cred_encoding_type_t type,	chunk_t *fp)
++{
++	bool success;
++
++	if (lib->encoding->get_cache(lib->encoding, type, this, fp))
++	{
++		return TRUE;
++	}
++
++	success = ml_dsa_fingerprint(this->pubkey, this->type, type, fp);
++	if (success)
++	{
++		lib->encoding->cache(lib->encoding, type, this, fp);
++	}
++
++	return success;
++}
++
++METHOD(private_key_t, get_encoding, bool,
++	private_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding)
++{
++	switch (type)
++	{
++		case PRIVKEY_ASN1_DER:
++		case PRIVKEY_PEM:
++		{
++			bool success = TRUE;
++			int oid = key_type_to_oid(this->type);
++
++			*encoding = asn1_wrap(ASN1_SEQUENCE, "cmm",
++							ASN1_INTEGER_0,
++							asn1_algorithmIdentifier(oid),
++							asn1_wrap(ASN1_OCTET_STRING, "m",
++								asn1_simple_object(ASN1_CONTEXT_S_0,
++												   this->keyseed))
++						);
++			if (type == PRIVKEY_PEM)
++			{
++				chunk_t asn1_encoding = *encoding;
++
++				success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM,
++								NULL, encoding, CRED_PART_PRIV_ASN1_DER,
++								asn1_encoding, CRED_PART_END);
++				chunk_clear(&asn1_encoding);
++			}
++
++			return success;
++		}
++		default:
++			return FALSE;
++	}
++}
++
++METHOD(private_key_t, get_ref, private_key_t*,
++	private_private_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(private_key_t, destroy, void,
++	private_private_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this);
++		DESTROY_IF(this->G);
++		DESTROY_IF(this->H);
++		chunk_clear(&this->keyseed);
++		chunk_clear(&this->privkey);
++		chunk_free(&this->pubkey);
++		free(this);
++	}
++}
++
++/**
++ * Generates an element of [-eta, eta] or rejects the sample.
++ *
++ * Algorithm 15 in FIPS 204.
++ */
++static bool coeff_from_half_byte(uint8_t b, uint8_t eta, int32_t *a)
++{
++	const int32_t eta_samples[] = {
++		2, 1, 0, -1, -2, 2, 1, 0, -1, -2, 2, 1, 0, -1, -2
++	};
++
++	if (eta == 2)
++	{
++		if (b >= 15)
++		{
++			return FALSE;  /* reject sample */
++		}
++		*a = eta_samples[b];
++	}
++	else if (eta == 4)
++	{
++		if (b >= 9)
++		{
++			return FALSE;  /* reject sample */
++		}
++		*a = 4 - (int32_t)b;
++	}
++
++	return TRUE;
++}
++
++/**
++ * Samples an element with coefficients in [-eta, eta] computed via
++ * rejection sampling on a SHAKE-256 output stream H.
++ *
++ * Algorithm 31 in FIPS 204.
++ */
++static bool rej_bounded_poly(private_private_key_t *this, chunk_t seed,
++							 ml_dsa_poly_t *a)
++{
++	uint8_t c, c0, c1;
++	u_int n = 0;
++
++	if (!this->H->set_seed(this->H, seed))
++	{
++		return FALSE;
++	}
++
++	while (n < ML_DSA_N)
++	{
++		if (!this->H->get_bytes(this->H, 1, &c))
++		{
++			return FALSE;
++		}
++
++		/* form half bytes */
++		c0 = c & 0x0f;
++		c1 = c >> 4;
++		if (coeff_from_half_byte(c0, this->params->eta, &a->f[n]))
++		{
++			if (++n == ML_DSA_N)
++			{
++				break;
++			}
++		}
++		if (coeff_from_half_byte(c1, this->params->eta, &a->f[n]))
++		{
++			++n;
++		}
++	}
++	return TRUE;
++}
++
++/**
++ * Samples vectors s1 and s2, each with polynomial coordinates whose coefficients
++ * are in the interval [-eta, eta].
++ *
++ * Algorithm 33 in FIPS 204.
++ */
++static bool expand_s(private_private_key_t *this, chunk_t rhoprime,
++					 ml_dsa_poly_t *s1, ml_dsa_poly_t *s2)
++{
++	chunk_t seed;
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	u_int i, j;
++	bool success = FALSE;
++
++	seed = chunk_alloca(2*ML_DSA_SEED_LEN + 2);
++	memcpy(seed.ptr, rhoprime.ptr, rhoprime.len);
++	seed.ptr[2*ML_DSA_SEED_LEN+1] = 0;
++
++	for (j = 0; j < l; j++)
++	{
++		seed.ptr[2*ML_DSA_SEED_LEN] = (uint8_t)j;
++	    if (!rej_bounded_poly(this, seed, &s1[j]))
++	    {
++			goto cleanup;
++	    }
++	}
++	for (i = 0; i < k; i++)
++	{
++		seed.ptr[2*ML_DSA_SEED_LEN] = (uint8_t)(l + i);
++	    if (!rej_bounded_poly(this, seed, &s2[i]))
++	    {
++			goto cleanup;
++	    }
++	}
++	success = TRUE;
++
++cleanup:
++	memwipe(seed.ptr, seed.len);
++
++	return success;
++}
++
++/**
++ * Encode the public key (pkEncode).
++ *
++ * Algorithm 22 in FIPS 204.
++ */
++static bool encode_public_key(private_private_key_t *this, chunk_t rho,
++							  ml_dsa_poly_t *t1)
++{
++	const u_int k = this->params->k;
++	u_int i, n;
++	ml_bitpacker_t *bitpacker;
++	chunk_t pk;
++
++	pk = this->pubkey;
++	memcpy(pk.ptr, rho.ptr, rho.len);
++	pk = chunk_skip(pk, rho.len);
++
++	/* pack the vector t1 into the public key blob */
++	bitpacker = ml_bitpacker_create(pk);
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->write_bits(bitpacker, t1[i].f[n], ML_DSA_T1_BITS))
++			{
++				bitpacker->destroy(bitpacker);
++				return FALSE;
++			}
++		}
++	}
++	bitpacker->destroy(bitpacker);
++
++	return TRUE;
++}
++
++/**
++ * Encode the secret key (skEncode).
++ *
++ * Algorithm 24 in FIPS 204.
++ */
++static bool encode_secret_key(private_private_key_t *this, chunk_t rho,
++							  chunk_t K, ml_dsa_poly_t *s1, ml_dsa_poly_t *s2,
++							  ml_dsa_poly_t *t0)
++{
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	const u_int d = this->params->d;
++	const u_int eta = this->params->eta;
++	u_int i, j, n;
++	ml_bitpacker_t *bitpacker;
++	chunk_t sk;
++	bool success = FALSE;
++
++	sk = this->privkey;
++	memcpy(sk.ptr, rho.ptr, rho.len);
++	sk = chunk_skip(sk, rho.len);
++
++	memcpy(sk.ptr, K.ptr, K.len);
++	sk = chunk_skip(sk, K.len);
++
++	/* compute tr as a SHAKE-256 digest over the public key blob
++	 * and put it in the private key blob
++	 */
++	if (!this->H->set_seed(this->H, this->pubkey) ||
++		!this->H->get_bytes(this->H, ML_DSA_TR_LEN, sk.ptr))
++	{
++		return FALSE;
++	}
++	sk = chunk_skip(sk, ML_DSA_TR_LEN);
++
++	/* pack the vectors s1, s2 and t0 into the private key blob*/
++	bitpacker = ml_bitpacker_create(sk);
++	for (j = 0; j < l; j++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->write_bits(bitpacker, eta - s1[j].f[n], d))
++			{
++				goto end;
++			}
++		}
++	}
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->write_bits(bitpacker, eta - s2[i].f[n], d))
++			{
++				goto end;
++			}
++		}
++	}
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->write_bits(bitpacker, (1 << (ML_DSA_D-1)) - t0[i].f[n],
++														 ML_DSA_D))
++			{
++				goto end;
++			}
++		}
++	}
++	success = TRUE;
++
++end:
++	bitpacker->destroy(bitpacker);
++
++	return success;
++}
++
++/**
++ * Generates a public/private key pair from a seed
++ *
++ * Algorithm 6 in FIPS 204.
++ */
++static bool generate_keypair(private_private_key_t *this, chunk_t keyseed)
++{
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	ml_dsa_poly_t a[k*l], s1[l], s1_hat[l], s2[k], t1[k], t0[k];
++	bool success = FALSE;
++
++	/**
++	 *  Mapping of seedbuf
++	 *
++	 *       0         32
++	 *       +---------+
++	 *  Init | keyseed |
++	 *       +---------+
++	 *       0          34
++	 *       +----------+
++	 *  In   |   seed   |
++	 *       +----------+
++	 *       0         32        64        96        128
++	 *       +---------+-------------------+---------+
++	 *  Out  |   rho   |      rhoprime     |    K    |
++	 *       +---------+-------------------+---------+
++	 */
++	uint8_t seedbuf[4*ML_DSA_SEED_LEN];
++	chunk_t seed =     { seedbuf,                       ML_DSA_SEED_LEN+2 };
++	chunk_t rho  =     { seedbuf,                       ML_DSA_SEED_LEN };
++	chunk_t rhoprime = { seedbuf +   ML_DSA_SEED_LEN, 2*ML_DSA_SEED_LEN };
++	chunk_t K =        { seedbuf + 3*ML_DSA_SEED_LEN,   ML_DSA_K_LEN };
++
++	/* keep a copy of the secret key seed */
++	this->keyseed = keyseed;
++
++	memcpy(seedbuf, keyseed.ptr, keyseed.len);
++	seedbuf[ML_DSA_SEED_LEN]   = this->params->k;
++	seedbuf[ML_DSA_SEED_LEN+1] = this->params->l;
++
++	if (!this->H->set_seed(this->H, seed) ||
++		!this->H->get_bytes(this->H, sizeof(seedbuf), seedbuf) ||
++		!ml_dsa_expand_a(this->params, this->G, rho, a) ||
++		!expand_s(this, rhoprime, s1, s2))
++	{
++		goto cleanup;
++	}
++
++	/* apply NTT to a copy of the s1 vector */
++	ml_dsa_poly_copy_vec(l, s1, s1_hat);
++	ml_dsa_poly_ntt_vec(l, s1_hat);
++
++	/* multiply vector s1_hat with matrix a in the NTT domain */
++	ml_dsa_poly_mult_mat(k, l, a, s1_hat, t1);
++
++	/* reduce the elements of vector t1 to the range -6283008 <= r <= 6283008 */
++	ml_dsa_poly_reduce_vec(k, t1);
++
++	/* apply the inverse NTT to vector t1 */
++	ml_dsa_poly_inv_ntt_vec(k, t1);
++
++	/* add error vector s2 to t1 */
++	ml_dsa_poly_add_vec(k, s2, t1, t1);
++
++	/* make all polynomial coefficients positive by conditionally adding q */
++	ml_dsa_poly_cond_add_q_vec(k, t1);
++
++	/* decomposes t1 into (t1, t0) such that t1 ≡ t1 * 2^d + t0 mod q */
++	ml_dsa_poly_power2round_vec(k, t1, t0, t1);
++
++	success = encode_public_key(this, rho, t1) &&
++			  encode_secret_key(this, rho, K, s1, s2, t0);
++
++cleanup:
++	memwipe(seedbuf, sizeof(seedbuf));
++	memwipe(a, sizeof(a));
++	memwipe(s1, sizeof(s1));
++	memwipe(s1_hat, sizeof(s1_hat));
++	memwipe(s2, sizeof(s2));
++	memwipe(t0, sizeof(t0));
++
++	return success;
++}
++
++/**
++ * Generic private constructor
++ */
++static private_private_key_t *create_instance(key_type_t type)
++{
++	private_private_key_t *this;
++	const ml_dsa_params_t *params;
++
++	params = ml_dsa_params_get(type);
++	if (!params)
++	{
++		return NULL;
++	}
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.sign = _sign,
++			.decrypt = _decrypt,
++			.get_keysize = _get_keysize,
++			.get_public_key = _get_public_key,
++			.equals = private_key_equals,
++			.belongs_to = private_key_belongs_to,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = private_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.params = params,
++		.pubkey = chunk_alloc(get_public_key_size(type)),
++		.privkey = chunk_alloc(params->privkey_len),
++		.G = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_128),
++		.H = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_256),
++		.ref = 1,
++	);
++
++	if (!this->G || !this->H)
++	{
++		destroy(this);
++		return NULL;
++	}
++
++	return this;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *ml_dsa_private_key_gen(key_type_t type, va_list args)
++{
++	private_private_key_t *this;
++	chunk_t seed;
++	rng_t *rng;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_KEY_SIZE:
++				/* just ignore the key size */
++				va_arg(args, u_int);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	rng = lib->crypto->create_rng(lib->crypto, RNG_TRUE);
++	if (!rng || !rng->allocate_bytes(rng, ML_DSA_SEED_LEN, &seed))
++	{
++		DESTROY_IF(rng);
++		return NULL;
++	}
++	rng->destroy(rng);
++
++	this = create_instance(type);
++	if (!this)
++	{
++		chunk_free(&seed);
++		return NULL;
++	}
++
++	if (!generate_keypair(this, seed))
++	{
++		destroy(this);
++		return NULL;
++	}
++	return &this->public;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *ml_dsa_private_key_load(key_type_t type, va_list args)
++{
++	private_private_key_t *this;
++	chunk_t priv = chunk_empty;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				priv = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (priv.len == 0 || !ml_dsa_type_supported(type))
++	{
++		return NULL;
++	}
++	if (priv.len == ML_DSA_SEED_LEN + 2 &&
++		priv.ptr[0] == 0x80 && priv.ptr[1] == ML_DSA_SEED_LEN)
++	{
++		priv = chunk_skip(priv, 2);
++	}
++	if (priv.len != ML_DSA_SEED_LEN)
++	{
++		DBG1(DBG_LIB, "error: the size of the loaded ML-DSA private key seed is "
++					  "%u bytes instead of %d bytes", priv.len, ML_DSA_SEED_LEN);
++		return NULL;
++	}
++
++	this = create_instance(type);
++	if (!this)
++	{
++		return NULL;
++	}
++
++	if (!generate_keypair(this, chunk_clone(priv)))
++	{
++		destroy(this);
++		return NULL;
++	}
++
++	return &this->public;
++}
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_private_key.h b/src/libstrongswan/plugins/ml/ml_dsa_private_key.h
+new file mode 100644
+index 00000000000..473cecdb692
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_private_key.h
+@@ -0,0 +1,48 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup ml_dsa_private_key ml_dsa_private_key
++ * @{ @ingroup ml_p
++ */
++
++#ifndef ML_DSA_PRIVATE_KEY_H_
++#define ML_DSA_PRIVATE_KEY_H_
++
++#include <credentials/builder.h>
++#include <credentials/keys/private_key.h>
++
++/**
++ * Generate an ML-DSA private key.
++ *
++ * @param type		key type, must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			generated key, NULL on failure
++ */
++private_key_t *ml_dsa_private_key_gen(key_type_t type, va_list args);
++
++/**
++ * Load an ML-DSA private key using.
++ *
++ * Accepts a BUILD_BLOB argument.
++ *
++ * @param type		key type, must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++private_key_t *ml_dsa_private_key_load(key_type_t type, va_list args);
++
++#endif /** ML_DSA_PRIVATE_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_public_key.c b/src/libstrongswan/plugins/ml/ml_dsa_public_key.c
+new file mode 100644
+index 00000000000..2d5d5f4bb5b
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_public_key.c
+@@ -0,0 +1,653 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen, strongSec GmbH
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include "ml_dsa_public_key.h"
++#include "ml_dsa_params.h"
++#include "ml_dsa_poly.h"
++#include "ml_bitpacker.h"
++
++#include <utils/debug.h>
++#include <asn1/asn1.h>
++
++typedef struct private_public_key_t private_public_key_t;
++
++/**
++ * Private data
++ */
++struct private_public_key_t {
++
++	/**
++	 * Public interface
++	 */
++	public_key_t public;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Parameter set.
++	 */
++	const ml_dsa_params_t *params;
++
++	/**
++	 * Public key
++	 */
++	chunk_t pubkey;
++
++	/**
++	 * SHAKE-128 instance.
++	 */
++	xof_t *G;
++
++	/**
++	 * SHAKE-256 instance.
++	 */
++	xof_t *H;
++
++	/**
++	 * Reference count
++	 */
++	refcount_t ref;
++};
++
++METHOD(public_key_t, get_type, key_type_t,
++	private_public_key_t *this)
++{
++	return this->type;
++}
++
++/**
++ * Decode the public key (pkDncode).
++ *
++ * Algorithm 23 in FIPS 204.
++ */
++static bool decode_public_key(private_public_key_t *this, chunk_t rho,
++							  ml_dsa_poly_t *t1)
++{
++	const u_int k = this->params->k;
++	u_int i, n;
++	ml_bitpacker_t *bitpacker;
++	chunk_t pk;
++
++	pk = this->pubkey;
++	memcpy(rho.ptr, pk.ptr, rho.len);
++	pk = chunk_skip(pk, rho.len);
++
++	/* unpack the vector t1 from the public key blob */
++	bitpacker = ml_bitpacker_create_from_data(pk);
++	for (i = 0; i < k; i++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->read_bits(bitpacker, &t1[i].f[n], ML_DSA_T1_BITS))
++			{
++				bitpacker->destroy(bitpacker);
++				return FALSE;
++			}
++		}
++	}
++	bitpacker->destroy(bitpacker);
++
++	return TRUE;
++}
++
++/**
++ * Decodes a signature (sigDecode).
++ *
++ * Algorithm 27 in FIPS 204.
++ */
++static bool decode_signature(private_public_key_t *this, chunk_t signature,
++							 chunk_t c_tilde, ml_dsa_poly_t *z, ml_dsa_poly_t *h)
++{
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	const u_int gamma1_exp = this->params->gamma1_exp;
++	const u_int omega = this->params->omega;
++	const size_t sig_len = this->params->sig_len;
++	ml_bitpacker_t *bitpacker;
++	u_int i, j, n, first, index = 0;
++	uint32_t value;
++	chunk_t sig;
++
++	if (signature.len != sig_len)
++	{
++		DBG1(DBG_LIB, "error: the size of the ML-DSA signature is %u bytes "
++					  "instead of %u bytes", signature.len, sig_len);
++		return FALSE;
++	}
++	sig = signature;
++
++	/* extract byte string c_tilde */
++	memcpy(c_tilde.ptr, sig.ptr, c_tilde.len);
++	sig = chunk_skip(sig, c_tilde.len);
++
++	/* decode vector of polynomials z from packed format */
++	bitpacker = ml_bitpacker_create_from_data(sig);
++	for (j = 0; j < l; j++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->read_bits(bitpacker, &value, 1 +  gamma1_exp))
++			{
++				bitpacker->destroy(bitpacker);
++				return FALSE;
++			}
++			z[j].f[n] = (1 << gamma1_exp) - (int32_t)value;
++		}
++	}
++	bitpacker->destroy(bitpacker);
++	sig = chunk_skip(sig, 32 * (1 + gamma1_exp) * l);
++
++	/* decode vector of polynomials with binary coefficients h (HintBitUnpack)
++	 * Algorithm 21 in FIPS 204.
++	 */
++	memset(h, 0x00, k * ML_DSA_N * sizeof(int32_t));
++
++	for (i = 0; i < k; i++)
++	{
++		if (sig.ptr[omega + i] < index)
++		{
++			DBG1(DBG_LIB, "error: signature with a decreasing maximum hint index");
++			return FALSE;
++		}
++		if (sig.ptr[omega + i] > omega)
++		{
++			DBG1(DBG_LIB, "error: signature with an oversized maximum hint index");
++			return FALSE;
++		}
++
++		first = index;
++		while (index < sig.ptr[omega + i])
++		{
++			if (index > first && sig.ptr[index-1] >= sig.ptr[index])
++			{
++				DBG1(DBG_LIB, "error: signature with non-increasing hint positions");
++				return FALSE;
++			}
++			h[i].f[sig.ptr[index++]] = 1;
++		}
++	}
++	while (index < omega)
++	{
++		if (sig.ptr[index++] != 0x00)
++		{
++			DBG1(DBG_LIB, "error: signature with non-zeroed unused hint bit");
++			return FALSE;
++		}
++	}
++
++	return TRUE;
++}
++
++/**
++ * Samples a polynomial with uniformly random coefficients in [0,Q-1]
++ * by performing rejection sampling on a SHAKE-128 output stream G.
++ *
++ * Algorithm 30 in FIPS 204.
++ */
++static bool rej_ntt_poly(xof_t *G, chunk_t seed, ml_dsa_poly_t *a)
++{
++	uint8_t c[3];
++	uint32_t t;
++	u_int n = 0;
++
++	if (!G->set_seed(G, seed))
++	{
++		return FALSE;
++	}
++
++	while (n < ML_DSA_N)
++	{
++		if (!G->get_bytes(G, sizeof(c), c))
++		{
++			return FALSE;
++		}
++
++		/* Algorithm 14 in FIPS 204 (CoeffFromThreeBytes) */
++		t = (uint32_t)c[2] << 16 | (uint32_t)c[1] << 8 | (uint32_t)c[0];
++		t &= 0x7fffff;
++
++		if (t < ML_DSA_Q)
++		{
++			a->f[n++] = t;
++		}
++	}
++
++	return TRUE;
++}
++
++/**
++ * Samples a k x l matrix A.
++ *
++ * Algorithm 32 in FIPS 204.
++ */
++bool ml_dsa_expand_a(const ml_dsa_params_t *params, xof_t *G, chunk_t rho,
++					 ml_dsa_poly_t *a)
++{
++	const u_int k = params->k;
++	const u_int l = params->l;
++	u_int i, j, ctr = 0;
++	chunk_t seed;
++
++	seed = chunk_alloca(ML_DSA_SEED_LEN + 2);
++	memcpy(seed.ptr, rho.ptr, rho.len);
++
++	for (i = 0; i < k; i++)
++	{
++		for (j = 0; j < l; j++)
++		{
++			seed.ptr[ML_DSA_SEED_LEN+1] = (uint8_t)i;
++			seed.ptr[ML_DSA_SEED_LEN]   = (uint8_t)j;
++
++			if (!rej_ntt_poly(G, seed, &a[ctr++]))
++			{
++				return FALSE;
++			}
++		}
++	}
++
++	return TRUE;
++}
++
++/**
++ * samples a polynomial c with coefficients from {-1, 0, 1}
++ * and Hamming weight tau <= 64.
++ *
++ * Algorithm 29 in FIPS 204.
++ */
++bool ml_dsa_sample_in_ball(xof_t *H, int32_t tau, chunk_t rho, ml_dsa_poly_t *c)
++{
++	uint8_t s[8], b;
++	uint64_t signs = 0;
++	u_int i;
++
++	if (!H->set_seed(H, rho) ||
++		!H->get_bytes(H, 8, s))
++	{
++		return FALSE;
++	}
++	for (i = 0; i < 8; i++)
++	{
++		signs |= (uint64_t)s[i] << 8*i;
++	}
++	for (i = 0; i < ML_DSA_N; i++)
++	{
++		c->f[i] = 0;
++	}
++	for (i = ML_DSA_N - tau; i < ML_DSA_N; i++)
++	{
++		do
++		{
++			if (!H->get_bytes(H, 1, &b))
++			{
++				return FALSE;
++			}
++		} while (b > i);
++
++		c->f[i] = c->f[b];
++		c->f[b] = 1 - 2*(signs & 1);
++		signs >>= 1;
++	}
++
++	return TRUE;
++}
++
++/**
++ * Encodes a polynomial vector w1 into a byte string.
++ *
++ * Algorithm 28 in FIPS 204.
++ */
++bool ml_dsa_w1_encode(u_int k, ml_dsa_poly_t *w1, chunk_t w1_enc, u_int d)
++{
++	ml_bitpacker_t *bitpacker;
++	u_int j, n;
++
++	bitpacker = ml_bitpacker_create(w1_enc);
++	for (j = 0; j < k; j++)
++	{
++		for (n = 0; n < ML_DSA_N; n++)
++		{
++			if (!bitpacker->write_bits(bitpacker, w1[j].f[n], d))
++			{
++				bitpacker->destroy(bitpacker);
++				return FALSE;
++			}
++		}
++	}
++	bitpacker->destroy(bitpacker);
++
++	return TRUE;
++}
++
++METHOD(public_key_t, verify, bool,
++	private_public_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t signature)
++{
++	const u_int k = this->params->k;
++	const u_int l = this->params->l;
++	const int32_t bound = (1 << this->params->gamma1_exp) - this->params->beta;
++	pqc_params_t pqc_params;
++	ml_dsa_poly_t a[k*l], t1[k], z[l], h[k], w1[k], c;
++	chunk_t rho, c_tilde, c_tilde2, w1_enc, tr, seed, mu;
++
++	if (key_type_from_signature_scheme(scheme) != this->type)
++	{
++		DBG1(DBG_LIB, "signature scheme %N not supported",
++					   signature_scheme_names, scheme);
++		return FALSE;
++	}
++
++	rho      = chunk_alloca(ML_DSA_SEED_LEN);
++	c_tilde  = chunk_alloca(this->params->lambda / 4);
++	c_tilde2 = chunk_alloca(this->params->lambda / 4);
++	w1_enc   = chunk_alloca(32 * this->params->gamma2_d * k);
++	tr       = chunk_alloca(ML_DSA_TR_LEN);
++	mu       = chunk_alloca(ML_DSA_MU_LEN);
++
++	if (!decode_public_key(this, rho, t1) ||
++		!decode_signature(this, signature, c_tilde, z, h) ||
++		!ml_dsa_poly_check_bound_vec(l, z, bound) ||
++		!ml_dsa_expand_a(this->params, this->G, rho, a))
++	{
++		return FALSE;
++	}
++
++	/* compute tr as a SHAKE-256 digest over the public key blob */
++	if (!this->H->set_seed(this->H, this->pubkey) ||
++		!this->H->get_bytes(this->H, ML_DSA_TR_LEN, tr.ptr))
++	{
++		return FALSE;
++	}
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		return FALSE;
++	}
++
++	/* compute message representative mu */
++	seed = chunk_cat("cmc", tr, pqc_params.pre_ctx, data);
++	if (!this->H->set_seed(this->H, seed) ||
++		!this->H->get_bytes(this->H, ML_DSA_MU_LEN, mu.ptr))
++	{
++		chunk_free(&seed);
++		return FALSE;
++	}
++	chunk_free(&seed);
++
++	/* verifier's challenge */
++	if (!ml_dsa_sample_in_ball(this->H, this->params->tau, c_tilde, &c))
++	{
++		return FALSE;
++	}
++
++	/* compute w1 = a * z - c * 2^d * t1 */
++ 	ml_dsa_poly_ntt(&c);
++	ml_dsa_poly_ntt_vec(l, z);
++	ml_dsa_poly_mult_mat(k, l, a, z, w1);
++	ml_dsa_poly_shift_left_vec(k, t1);
++	ml_dsa_poly_ntt_vec(k, t1);
++	ml_dsa_poly_mult_const_vec(k, &c, t1, t1);
++	ml_dsa_poly_sub_vec(k, w1, t1, w1);
++	ml_dsa_poly_reduce_vec(k, w1);
++	ml_dsa_poly_inv_ntt_vec(k, w1);
++
++	/* reconstruct w1 */
++	ml_dsa_poly_cond_add_q_vec(k, w1);
++	ml_dsa_poly_use_hint_vec(k, w1, h, w1, this->params->gamma2);
++
++	/* compress the w1 vector into a byte string */
++	if (!ml_dsa_w1_encode(k, w1, w1_enc, this->params->gamma2_d))
++	{
++		return FALSE;
++	}
++
++	/* compute commitment hash c_tilde2 */
++	seed = chunk_cat("cc", mu, w1_enc);
++	if (!this->H->set_seed(this->H, seed) ||
++		!this->H->get_bytes(this->H, this->params->lambda/4, c_tilde2.ptr))
++	{
++		chunk_free(&seed);
++		return FALSE;
++	}
++	chunk_free(&seed);
++
++	return chunk_equals_const(c_tilde2, c_tilde);
++}
++
++METHOD(public_key_t, encrypt_, bool,
++	private_public_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "encryption scheme %N not supported", encryption_scheme_names,
++		 scheme);
++	return FALSE;
++}
++
++METHOD(public_key_t, get_keysize, int,
++	private_public_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++/**
++ * Generate two types of ML-DSA fingerprints.
++ */
++bool ml_dsa_fingerprint(chunk_t pubkey, key_type_t type,
++						cred_encoding_type_t enc_type, chunk_t *fp)
++{
++	chunk_t encoding;
++	hasher_t *hasher;
++
++	*fp = chunk_empty;
++
++	switch (enc_type)
++	{
++		case KEYID_PUBKEY_SHA1:
++			encoding = chunk_clone(pubkey);
++			break;
++		case KEYID_PUBKEY_INFO_SHA1:
++			encoding = public_key_info_encode(pubkey, key_type_to_oid(type));
++			break;
++		default:
++			return FALSE;
++	}
++
++	hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
++	if (!hasher || !hasher->allocate_hash(hasher, encoding, fp))
++	{
++		DBG1(DBG_LIB, "SHA1 hash algorithm not supported");
++		DESTROY_IF(hasher);
++		chunk_free(&encoding);
++		return FALSE;
++	}
++	hasher->destroy(hasher);
++	chunk_free(&encoding);
++
++	return TRUE;
++}
++
++METHOD(public_key_t, get_fingerprint, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *fp)
++{
++	bool success;
++
++	if (lib->encoding->get_cache(lib->encoding, type, this, fp))
++	{
++		return TRUE;
++	}
++
++	success = ml_dsa_fingerprint(this->pubkey, this->type, type, fp);
++	if (success)
++	{
++		lib->encoding->cache(lib->encoding, type, this, fp);
++	}
++
++	return success;
++}
++
++METHOD(public_key_t, get_encoding, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding)
++{
++	bool success = TRUE;
++	int oid;
++
++	oid = key_type_to_oid(this->type);
++	*encoding = public_key_info_encode(this->pubkey, oid);
++
++	if (type != PUBKEY_SPKI_ASN1_DER)
++	{
++		chunk_t asn1_encoding = *encoding;
++
++		success = lib->encoding->encode(lib->encoding, type,
++						NULL, encoding, CRED_PART_PUB_ASN1_DER,
++						asn1_encoding, CRED_PART_END);
++		chunk_clear(&asn1_encoding);
++	}
++
++	return success;
++}
++
++METHOD(public_key_t, get_ref, public_key_t*,
++	private_public_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(public_key_t, destroy, void,
++	private_public_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this);
++		DESTROY_IF(this->G);
++		DESTROY_IF(this->H);
++		chunk_free(&this->pubkey);
++		free(this);
++	}
++}
++
++/**
++ * Generic private constructor
++ */
++static private_public_key_t *create_empty(key_type_t type, chunk_t pubkey)
++{
++	private_public_key_t *this;
++	const ml_dsa_params_t *params;
++
++	params = ml_dsa_params_get(type);
++	if (!params)
++	{
++		return NULL;
++	}
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.verify = _verify,
++			.encrypt = _encrypt_,
++			.get_keysize = _get_keysize,
++			.equals = public_key_equals,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = public_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.params = params,
++		.pubkey = chunk_clone(pubkey),
++		.G = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_128),
++		.H = lib->crypto->create_xof(lib->crypto, XOF_SHAKE_256),
++		.ref = 1,
++	);
++
++	if (!this->G || !this->H)
++	{
++		destroy(this);
++		return NULL;
++	}
++
++	return this;
++}
++
++/**
++ * Check if ML-DSA key type is supported.
++ */
++bool ml_dsa_type_supported(key_type_t type)
++{
++	switch (type)
++	{
++		case KEY_ML_DSA_44:
++		case KEY_ML_DSA_65:
++		case KEY_ML_DSA_87:
++			return TRUE;
++		default:
++			return FALSE;
++	}
++}
++
++/*
++ * Described in header
++ */
++public_key_t *ml_dsa_public_key_load(key_type_t type, va_list args)
++{
++	private_public_key_t *this;
++	chunk_t pkcs1, blob = chunk_empty;
++	size_t pubkey_len;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				blob = va_arg(args, chunk_t);
++				continue;
++			case BUILD_BLOB_ASN1_DER:
++				pkcs1 = va_arg(args, chunk_t);
++				type = public_key_info_decode(pkcs1, &blob);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (!ml_dsa_type_supported(type) || blob.len == 0)
++	{
++		return NULL;
++	}
++	pubkey_len = get_public_key_size(type);
++	if (blob.len != pubkey_len)
++	{
++		DBG1(DBG_LIB, "the size of the loaded ML-DSA public key is %u bytes "
++					  "instead of %u bytes", blob.len, pubkey_len);
++		return NULL;
++	}
++
++	this = create_empty(type, blob);
++	if (!this)
++	{
++		return NULL;
++	}
++
++	return &this->public;
++}
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_public_key.h b/src/libstrongswan/plugins/ml/ml_dsa_public_key.h
+new file mode 100644
+index 00000000000..4daf0344b65
+--- /dev/null
++++ b/src/libstrongswan/plugins/ml/ml_dsa_public_key.h
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2024 Andreas Steffen, strongSec GmbH
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup ml_dsa_public_key ml_dsa_public_key
++ * @{ @ingroup ml_p
++ */
++
++#ifndef ML_DSA_PUBLIC_KEY_H_
++#define ML_DSA_PUBLIC_KEY_H_
++
++#include <credentials/builder.h>
++#include <credentials/keys/public_key.h>
++
++/**
++ * Load an ML-DSA public key.
++ *
++ * Accepts a BUILD_BLOB or BUILD_BLOB_ASN1_DER argument.
++ *
++ * @param type		key type, must be KEY_ML_DSA_44, KEY_ML_DSA_65 or KEY_ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++public_key_t *ml_dsa_public_key_load(key_type_t type, va_list args);
++
++#endif /** ML_DSA_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/ml/ml_kem.c b/src/libstrongswan/plugins/ml/ml_kem.c
+index 9da72ea6114..da3b9672870 100644
+--- a/src/libstrongswan/plugins/ml/ml_kem.c
++++ b/src/libstrongswan/plugins/ml/ml_kem.c
+@@ -16,7 +16,7 @@
+ 
+ #include "ml_bitpacker.h"
+ #include "ml_kem.h"
+-#include "ml_params.h"
++#include "ml_kem_params.h"
+ #include "ml_poly.h"
+ #include "ml_utils.h"
+ 
+diff --git a/src/libstrongswan/plugins/ml/ml_params.c b/src/libstrongswan/plugins/ml/ml_kem_params.c
+similarity index 98%
+rename from src/libstrongswan/plugins/ml/ml_params.c
+rename to src/libstrongswan/plugins/ml/ml_kem_params.c
+index 89b1e6dbe56..d926ea4eaee 100644
+--- a/src/libstrongswan/plugins/ml/ml_params.c
++++ b/src/libstrongswan/plugins/ml/ml_kem_params.c
+@@ -14,7 +14,7 @@
+  * for more details.
+  */
+ 
+-#include "ml_params.h"
++#include "ml_kem_params.h"
+ 
+ /*
+  * Described in header
+diff --git a/src/libstrongswan/plugins/ml/ml_params.h b/src/libstrongswan/plugins/ml/ml_kem_params.h
+similarity index 94%
+rename from src/libstrongswan/plugins/ml/ml_params.h
+rename to src/libstrongswan/plugins/ml/ml_kem_params.h
+index 280aed6155a..bc15c5b73bf 100644
+--- a/src/libstrongswan/plugins/ml/ml_params.h
++++ b/src/libstrongswan/plugins/ml/ml_kem_params.h
+@@ -15,12 +15,12 @@
+  */
+ 
+ /**
+- * @defgroup ml_params ml_params
++ * @defgroup ml_kem_params ml_kem_params
+  * @{ @ingroup ml_p
+  */
+ 
+-#ifndef ML_PARAMS_H_
+-#define ML_PARAMS_H_
++#ifndef ML_KEM_PARAMS_H_
++#define ML_KEM_PARAMS_H_
+ 
+ #include <crypto/key_exchange.h>
+ 
+@@ -107,4 +107,4 @@ extern const uint16_t ml_kem_zetas[128];
+  */
+ const ml_kem_params_t *ml_kem_params_get(key_exchange_method_t method);
+ 
+-#endif /** ML_PARAMS_H_ @}*/
++#endif /** ML_KEM_PARAMS_H_ @}*/
+diff --git a/src/libstrongswan/plugins/ml/ml_plugin.c b/src/libstrongswan/plugins/ml/ml_plugin.c
+index 2c0432b45e9..1d77e7dccbe 100644
+--- a/src/libstrongswan/plugins/ml/ml_plugin.c
++++ b/src/libstrongswan/plugins/ml/ml_plugin.c
+@@ -19,6 +19,8 @@
+ #include <plugins/plugin.h>
+ 
+ #include "ml_kem.h"
++#include "ml_dsa_public_key.h"
++#include "ml_dsa_private_key.h"
+ 
+ typedef struct private_plugin_t private_plugin_t;
+ 
+@@ -39,6 +41,25 @@ METHOD(plugin_t, get_name, char*,
+ 	return "ml";
+ }
+ 
++/**
++ * Helper macros to declare dependencies for ML-DSA.
++ */
++#define ML_DSA_DEPS \
++	PLUGIN_DEPENDS(XOF, XOF_SHAKE_128), \
++	PLUGIN_DEPENDS(XOF, XOF_SHAKE_256)
++
++#define ML_DSA_PUBKEY_DEPS \
++	ML_DSA_DEPS, \
++	PLUGIN_DEPENDS(HASHER, HASH_SHA1)
++
++#define ML_DSA_PRIVKEY_DEPS \
++	ML_DSA_DEPS, \
++	PLUGIN_DEPENDS(RNG, RNG_STRONG)
++
++#define ML_DSA_PRIVKEY_GEN_DEPS \
++	ML_DSA_PRIVKEY_DEPS, \
++	PLUGIN_DEPENDS(RNG, RNG_TRUE)
++
+ METHOD(plugin_t, get_features, int,
+ 	private_plugin_t *this, plugin_feature_t *features[])
+ {
+@@ -62,6 +83,41 @@ METHOD(plugin_t, get_features, int,
+ 				PLUGIN_DEPENDS(XOF, XOF_SHAKE_128),
+ 				PLUGIN_DEPENDS(XOF, XOF_SHAKE_256),
+ 				PLUGIN_DEPENDS(RNG, RNG_STRONG),
++		PLUGIN_REGISTER(PUBKEY, ml_dsa_public_key_load, TRUE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
++				ML_DSA_PUBKEY_DEPS,
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
++				ML_DSA_PUBKEY_DEPS,
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
++				ML_DSA_PUBKEY_DEPS,
++			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
++				ML_DSA_PUBKEY_DEPS,
++		PLUGIN_REGISTER(PRIVKEY, ml_dsa_private_key_load, TRUE),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++				ML_DSA_PRIVKEY_DEPS,
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++				ML_DSA_PRIVKEY_DEPS,
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++				ML_DSA_PRIVKEY_DEPS,
++		PLUGIN_REGISTER(PRIVKEY_GEN, ml_dsa_private_key_gen, FALSE),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_44),
++				ML_DSA_PRIVKEY_GEN_DEPS,
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_65),
++				ML_DSA_PRIVKEY_GEN_DEPS,
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_87),
++				ML_DSA_PRIVKEY_GEN_DEPS,
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_44),
++			ML_DSA_PRIVKEY_DEPS,
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_65),
++			ML_DSA_PRIVKEY_DEPS,
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_87),
++			ML_DSA_PRIVKEY_DEPS,
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_44),
++			ML_DSA_PUBKEY_DEPS,
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_65),
++			ML_DSA_PUBKEY_DEPS,
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_87),
++			ML_DSA_PUBKEY_DEPS,
+ 	};
+ 	*features = f;
+ 	return countof(f);
+diff --git a/src/libstrongswan/plugins/ml/ml_poly.h b/src/libstrongswan/plugins/ml/ml_poly.h
+index 3863598edb2..510282c83d4 100644
+--- a/src/libstrongswan/plugins/ml/ml_poly.h
++++ b/src/libstrongswan/plugins/ml/ml_poly.h
+@@ -22,7 +22,7 @@
+ #ifndef ML_POLY_H_
+ #define ML_POLY_H_
+ 
+-#include "ml_params.h"
++#include "ml_kem_params.h"
+ 
+ typedef struct ml_poly_t ml_poly_t;
+ 
+diff --git a/src/libstrongswan/plugins/ml/ml_utils.c b/src/libstrongswan/plugins/ml/ml_utils.c
+index 7b92f53aef5..cc74c3900e2 100644
+--- a/src/libstrongswan/plugins/ml/ml_utils.c
++++ b/src/libstrongswan/plugins/ml/ml_utils.c
+@@ -53,3 +53,75 @@ void ml_write_bytes_le(uint8_t *buf, size_t len, uint32_t val)
+ 		val >>= 8;
+ 	}
+ }
++
++/*
++ * Described in header
++ */
++void ml_decompose(int32_t a, int32_t *a0, int32_t *a1, int32_t gamma2)
++{
++	int32_t t0, t1;
++
++	t1  = (a + 127) >> 7;
++
++	if (gamma2 == (ML_DSA_Q-1)/32)
++	{
++		t1  = (t1 * 1025 + (1 << 21)) >> 22;
++		t1 &= 15;
++	}
++	else
++	{
++		t1  = (t1 * 11275 + (1 << 23)) >> 24;
++		t1 ^= ((43 - t1) >> 31) & t1;
++	}
++
++	t0 = a - t1 * 2 * gamma2;
++	t0 -= (((ML_DSA_Q-1)/2 - t0) >> 31) & ML_DSA_Q;
++
++	*a0 = t0;
++	*a1 = t1;
++}
++
++/*
++ * Described in header
++ */
++int32_t ml_use_hint(int32_t a, int32_t hint, int32_t gamma2)
++{
++	int32_t a0, a1;
++
++	ml_decompose(a, &a0, &a1, gamma2);
++
++	if (hint == 0)
++	{
++		return a1;
++	}
++	if (gamma2 == (ML_DSA_Q-1)/32)
++	{
++		if (a0 > 0)
++		{
++			return (a1 + 1) & 15;
++		}
++		else
++		{
++			return (a1 - 1) & 15;
++		}
++	}
++	else
++    {
++		if (a0 > 0)
++		{
++			return (a1 == 43) ?  0 : a1 + 1;
++		}
++		else
++		{
++			return (a1 ==  0) ? 43 : a1 - 1;
++		}
++    }
++}
++
++/*
++ * Described in header
++ */
++int32_t ml_make_hint(int32_t a0, int32_t a1, int32_t gamma2)
++{
++	return (a0 > gamma2 || a0 < -gamma2 || (a0 == -gamma2 && a1 != 0)) ? 1 : 0;
++}
+\ No newline at end of file
+diff --git a/src/libstrongswan/plugins/ml/ml_utils.h b/src/libstrongswan/plugins/ml/ml_utils.h
+index 6f526225967..41b9e8c5317 100644
+--- a/src/libstrongswan/plugins/ml/ml_utils.h
++++ b/src/libstrongswan/plugins/ml/ml_utils.h
+@@ -22,7 +22,8 @@
+ #ifndef ML_UTILS_H_
+ #define ML_UTILS_H_
+ 
+-#include "ml_params.h"
++#include "ml_kem_params.h"
++#include "ml_dsa_params.h"
+ 
+ /**
+  * Returns a mod q for a in [0,2*q) in constant time.
+@@ -38,6 +39,21 @@ static inline uint16_t ml_reduce_modq(uint16_t a)
+ 	return (mask & a) | (~mask & diff);
+ }
+ 
++/**
++ * Computes a * 2^-32 mod q montgomery reduction
++ *
++ * Algorithm 49 in FIPS 204.
++ */
++static inline int32_t ml_montgomery_reduce(int64_t a)
++{
++	int32_t r;
++
++	r = (int64_t)(int32_t)a * ML_DSA_QINV;
++	r = (a - (int64_t)r * ML_DSA_Q) >> 32;
++
++	return r;
++}
++
+ /**
+  * Used to assign the given value based on a condition in constant time and
+  * without branching.
+@@ -66,4 +82,41 @@ uint32_t ml_read_bytes_le(uint8_t *buf, size_t len);
+  */
+ void ml_write_bytes_le(uint8_t *buf, size_t len, uint32_t val);
+ 
++/**
++ * Decompose a into (a1, a0) such that a ≡ a1 * (2 * gamma2) + a0 mod q.
++ *
++ * Algorithm 36 of FIPS 204.
++ *
++ * @param a			input value to be decomposed
++ * @param a0		low  bits of a
++ * @param a1		high bits of a
++ * @param gamma2	parameter gamma2
++ */
++void ml_decompose(int32_t a, int32_t *a0, int32_t *a1, int32_t gamma2);
++
++/**
++ * Return the high bits a1 of a adjusted according to hint h.
++ *
++ * Algorithm 40 of FIPS 204.
++ *
++ * @param a			input value to be adjusted
++ * @param hint		hint (0 or 1)
++ * @param gamma2	parameter gamma2
++ * @return			adjusted high bits of a
++ */
++int32_t ml_use_hint(int32_t a, int32_t hint, int32_t gamma2);
++
++/**
++ * Compute a hint bit indicating whether the low bits a0 of the
++ * input element overflow into the high bits a1.
++ *
++ * Algorithm 39 in FIPS 204.
++ *
++ * @param a0		low bits
++ * @param a1		high bits
++ * @param gamma2	parameter gamma2
++ * @return			hint bit (0 or 1)
++ */
++int32_t ml_make_hint(int32_t a0, int32_t a1, int32_t gamma2);
++
+ #endif /** ML_UTILS_H_ @}*/
+
+diff --git a/testing/hosts/winnetou/etc/ca/generate-crl b/testing/hosts/winnetou/etc/ca/generate-crl
+index 2c4067a84aa..a29b5be4794 100755
+--- a/testing/hosts/winnetou/etc/ca/generate-crl
++++ b/testing/hosts/winnetou/etc/ca/generate-crl
+@@ -131,6 +131,14 @@ cd /etc/ca/ed25519
+ pki --signcrl --cakey strongswanKey.pem --cacert strongswanCert.pem \
+     > ${ROOT}/strongswan_ed25519.crl
+ 
++##
++# strongSwan ML-DSA Root CA
++cd /etc/ca/ml_dsa
++
++# generate CRL for strongSwan ML-DSA Root CA
++pki --signcrl --cakey strongswanKey.pem --cacert strongswanCert.pem \
++    > ${ROOT}/strongswan_ml_dsa.crl
++
+ ##
+ # strongSwan Monster Root CA
+ cd /etc/ca/monster
+diff --git a/testing/hosts/winnetou/etc/strongswan.conf b/testing/hosts/winnetou/etc/strongswan.conf
+index aec8d76ce49..e99e14d0bed 100644
+--- a/testing/hosts/winnetou/etc/strongswan.conf
++++ b/testing/hosts/winnetou/etc/strongswan.conf
+@@ -1,5 +1,5 @@
+ # strongswan.conf - strongSwan configuration file
+ 
+ pki {
+-  load = random drbg pem sha1 sha2 sha3 pkcs1 pkcs8 pem gmp mgf1 curve25519 x509 openssl
++  load = random drbg pem sha1 sha2 sha3 pkcs1 pkcs8 pem gmp mgf1 curve25519 x509 ml openssl
+ }
+diff --git a/testing/scripts/build-certs-chroot b/testing/scripts/build-certs-chroot
+index 0cafb99ce55..91de11d580a 100755
+--- a/testing/scripts/build-certs-chroot
++++ b/testing/scripts/build-certs-chroot
+@@ -85,6 +85,11 @@ ED25519_KEY="${ED25519_DIR}/strongswanKey.pem"
+ ED25519_CERT="${ED25519_DIR}/strongswanCert.pem"
+ ED25519_CDP="http://crl.strongswan.org/strongswan_ed25519.crl"
+ #
++ML_DSA_DIR="${CA_DIR}/ml_dsa"
++ML_DSA_KEY="${ML_DSA_DIR}/strongswanKey.pem"
++ML_DSA_CERT="${ML_DSA_DIR}/strongswanCert.pem"
++ML_DSA_CDP="http://crl.strongswan.org/strongswan_ml_dsa.crl"
++#
+ MONSTER_DIR="${CA_DIR}/monster"
+ MONSTER_KEY="${MONSTER_DIR}/strongswanKey.pem"
+ MONSTER_CERT="${MONSTER_DIR}/strongswanCert.pem"
+@@ -112,6 +117,7 @@ mkdir -p ${ECDSA_DIR}/certs
+ mkdir -p ${RFC3779_DIR}/certs
+ mkdir -p ${SHA3_RSA_DIR}/certs
+ mkdir -p ${ED25519_DIR}/certs
++mkdir -p ${ML_DSA_DIR}/certs
+ mkdir -p ${MONSTER_DIR}/certs
+ 
+ ################################################################################
+@@ -1573,6 +1579,89 @@ pki --issue --cakey ${ED25519_KEY} --cacert ${ED25519_CERT} --type ed25519 \
+     --crl ${ED25519_CDP} --outform pem > ${TEST_CERT}
+ cp ${TEST_CERT} ${ED25519_DIR}/certs/${SERIAL}.pem
+ 
++################################################################################
++# strongSwan ML-DSA Root CA                                                    #
++################################################################################
++
++# Generate strongSwan ML-DSA Root CA
++pki --gen  --type mldsa87 --outform pem > ${ML_DSA_KEY}
++pki --self --type priv --in ${ML_DSA_KEY} \
++    --not-before "${START}" --not-after "${CA_END}" --ca \
++    --dn "C=CH, O=${PROJECT}, CN=strongSwan ML-DSA Root CA" \
++    --outform pem > ${ML_DSA_CERT}
++
++# Put a copy in the ikev2/rw-ml-kem-dsa scenario
++TEST="${TEST_DIR}/ikev2/rw-ml-kem-dsa"
++mkdir -p ${TEST}/hosts/moon/${SWANCTL_DIR}/x509ca
++mkdir -p ${TEST}/hosts/carol/${SWANCTL_DIR}/x509ca
++mkdir -p ${TEST}/hosts/dave/${SWANCTL_DIR}/x509ca
++cp ${ML_DSA_CERT} ${TEST}/hosts/moon/${SWANCTL_DIR}/x509ca
++cp ${ML_DSA_CERT} ${TEST}/hosts/carol/${SWANCTL_DIR}/x509ca
++cp ${ML_DSA_CERT} ${TEST}/hosts/dave/${SWANCTL_DIR}/x509ca
++
++# Generate a moon ML-DSA certificate
++MOON_KEY="${TEST}/hosts/moon/${SWANCTL_DIR}/pkcs8/moonKey.pem"
++MOON_CERT="${TEST}/hosts/moon/${SWANCTL_DIR}/x509/moonCert.pem"
++CN="moon.strongswan.org"
++SERIAL="01"
++mkdir -p ${TEST}/hosts/moon/${SWANCTL_DIR}/pkcs8
++mkdir -p ${TEST}/hosts/moon/${SWANCTL_DIR}/x509
++pki --gen --type mldsa65 --outform pem > ${MOON_KEY}
++pki --issue --cakey ${ML_DSA_KEY} --cacert ${ML_DSA_CERT} --type priv \
++    --in ${MOON_KEY} --not-before "${START}" --not-after "${EE_END}" --san ${CN} \
++    --serial ${SERIAL} --dn "C=CH, O=${PROJECT}, OU=ML-DSA, CN=${CN}" \
++    --flag "serverAuth" --crl ${ML_DSA_CDP} --outform pem > ${MOON_CERT}
++cp ${MOON_CERT} ${ML_DSA_DIR}/certs/${SERIAL}.pem
++
++# Generate a carol ML-DSA certificate
++CAROL_KEY="${TEST}/hosts/carol/${SWANCTL_DIR}/pkcs8/carolKey.pem"
++CAROL_CERT="${TEST}/hosts/carol/${SWANCTL_DIR}/x509/carolCert.pem"
++CN="carol@strongswan.org"
++SERIAL="02"
++mkdir -p ${TEST}/hosts/carol/${SWANCTL_DIR}/pkcs8
++mkdir -p ${TEST}/hosts/carol/${SWANCTL_DIR}/x509
++pki --gen --type mldsa44 --outform pem > ${CAROL_KEY}
++pki --issue --cakey ${ML_DSA_KEY} --cacert ${ML_DSA_CERT} --type priv \
++    --in ${CAROL_KEY} --not-before "${START}" --not-after "${EE_END}" --san ${CN} \
++    --serial ${SERIAL} --dn "C=CH, O=${PROJECT}, OU=ML-DSA, CN=${CN}" \
++    --flag "clientAuth" --crl ${ML_DSA_CDP} --outform pem > ${CAROL_CERT}
++cp ${CAROL_CERT} ${ML_DSA_DIR}/certs/${SERIAL}.pem
++
++# Generate a dave ML-DSA certificate
++DAVE_KEY="${TEST}/hosts/dave/${SWANCTL_DIR}/pkcs8/daveKey.pem"
++DAVE_CERT="${TEST}/hosts/dave/${SWANCTL_DIR}/x509/daveCert.pem"
++CN="dave@strongswan.org"
++SERIAL="03"
++mkdir -p ${TEST}/hosts/dave/${SWANCTL_DIR}/pkcs8
++mkdir -p ${TEST}/hosts/dave/${SWANCTL_DIR}/x509
++pki --gen --type mldsa44 --outform pem > ${DAVE_KEY}
++pki --issue --cakey ${ML_DSA_KEY} --cacert ${ML_DSA_CERT} --type priv \
++    --in ${DAVE_KEY} --not-before "${START}" --not-after "${EE_END}" --san ${CN} \
++    --serial ${SERIAL} --dn "C=CH, O=${PROJECT}, OU=ML-DSA, CN=${CN}" \
++    --flag "clientAuth" --crl ${ML_DSA_CDP} --outform pem > ${DAVE_CERT}
++cp ${DAVE_CERT} ${ML_DSA_DIR}/certs/${SERIAL}.pem
++
++# Put a copy in the botan and wolfssl rw-ml-kem scenarios
++for d in botan wolfssl
++do
++  TEST="${TEST_DIR}/${d}/rw-ml-kem-dsa"
++  cd ${TEST}/hosts/moon/${SWANCTL_DIR}
++  mkdir -p pkcs8 x509 x509ca
++  cp ${MOON_KEY}     pkcs8
++  cp ${MOON_CERT}    x509
++  cp ${ML_DSA_CERT}  x509ca
++  cd ${TEST}/hosts/carol/${SWANCTL_DIR}
++  mkdir -p pkcs8 x509 x509ca
++  cp ${CAROL_KEY}    pkcs8
++  cp ${CAROL_CERT}   x509
++  cp ${ML_DSA_CERT}  x509ca
++  cd ${TEST}/hosts/dave/${SWANCTL_DIR}
++  mkdir -p pkcs8 x509 x509ca
++  cp ${DAVE_KEY}     pkcs8
++  cp ${DAVE_CERT}    x509
++  cp ${ML_DSA_CERT}  x509ca
++done
++
+ ################################################################################
+ # strongSwan Monster Root CA                                                   #
+ ################################################################################
+diff --git a/testing/scripts/recipes/012_wolfssl.mk b/testing/scripts/recipes/012_wolfssl.mk
+index be5fef14dcf..029fee16abf 100644
+--- a/testing/scripts/recipes/012_wolfssl.mk
++++ b/testing/scripts/recipes/012_wolfssl.mk
+@@ -31,6 +31,7 @@ CONFIG_OPTS = \
+ 	--enable-rsapss \
+ 	--enable-sha3 \
+ 	--enable-shake256 \
++	--enable-dilithium \
+ 	--with-max-rsa-bits=8192
+ 
+ all: install
+diff --git a/testing/tests/botan/rw-ml-kem-dsa/description.txt b/testing/tests/botan/rw-ml-kem-dsa/description.txt
+new file mode 100755
+index 00000000000..1849ae2e281
+--- /dev/null
++++ b/testing/tests/botan/rw-ml-kem-dsa/description.txt
+@@ -0,0 +1,11 @@
++The roadwarrior <b>carol</b> and the gateway <b>moon</b> use the <b>botan</b>
++plugin based on the <b>Botan</b> library for all cryptographical functions whereas
++roadwarrior <b>dave</b> uses the <b>openssl</b> and <b>ml</b> plugins. The
++authentication is based on <b>ML-DSA</b>-type X.509 certificates and the key
++exchange on <b>x25519-ke1_mlkem512</b> for <b>carol</b> and <b>ecp384-ke1_mlkem786</b>
++for <b>dave</b>.
++<p/>
++Upon the successful establishment of the IPsec tunnels, the updown script
++automatically inserts iptables-based firewall rules that let pass the tunneled traffic.
++In order to test both tunnel and firewall, both <b>carol</b> and <b>dave</b> ping
++the client <b>alice</b> behind the gateway <b>moon</b>.
+diff --git a/testing/tests/botan/rw-mlkem/evaltest.dat b/testing/tests/botan/rw-ml-kem-dsa/evaltest.dat
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/evaltest.dat
+rename to testing/tests/botan/rw-ml-kem-dsa/evaltest.dat
+diff --git a/testing/tests/botan/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf b/testing/tests/botan/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf
+new file mode 100755
+index 00000000000..11c08c6be28
+--- /dev/null
++++ b/testing/tests/botan/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = botan pem x509 pubkey
++}
++
++charon-systemd {
++  load = nonce botan pem x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/botan/rw-mlkem/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/hosts/carol/etc/swanctl/swanctl.conf
+rename to testing/tests/botan/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/botan/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf b/testing/tests/botan/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf
+new file mode 100755
+index 00000000000..07d28859b39
+--- /dev/null
++++ b/testing/tests/botan/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem ml x509 pubkey openssl random
++}
++
++charon-systemd {
++  load = nonce pkcs8 ml x509 openssl pem revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/botan/rw-mlkem/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/hosts/dave/etc/swanctl/swanctl.conf
+rename to testing/tests/botan/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/botan/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf b/testing/tests/botan/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf
+new file mode 100755
+index 00000000000..f217ed4cdef
+--- /dev/null
++++ b/testing/tests/botan/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem botan x509 pubkey
++}
++
++charon-systemd {
++  load = nonce test-vectors botan pem x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/botan/rw-mlkem/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/hosts/moon/etc/swanctl/swanctl.conf
+rename to testing/tests/botan/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/botan/rw-ml-kem-dsa/posttest.dat b/testing/tests/botan/rw-ml-kem-dsa/posttest.dat
+new file mode 100755
+index 00000000000..f3b4dc9995c
+--- /dev/null
++++ b/testing/tests/botan/rw-ml-kem-dsa/posttest.dat
+@@ -0,0 +1,11 @@
++carol::swanctl --terminate --ike home
++dave::swanctl --terminate --ike home
++carol::systemctl stop strongswan
++dave::systemctl stop strongswan
++moon::systemctl stop strongswan
++moon::rm /etc/swanctl/pkcs8/*
++carol::rm /etc/swanctl/pkcs8/*
++dave::rm /etc/swanctl/pkcs8/*
++moon::iptables-restore < /etc/iptables.flush
++carol::iptables-restore < /etc/iptables.flush
++dave::iptables-restore < /etc/iptables.flush
+diff --git a/testing/tests/botan/rw-ml-kem-dsa/pretest.dat b/testing/tests/botan/rw-ml-kem-dsa/pretest.dat
+new file mode 100755
+index 00000000000..85426cbb491
+--- /dev/null
++++ b/testing/tests/botan/rw-ml-kem-dsa/pretest.dat
+@@ -0,0 +1,14 @@
++moon::iptables-restore < /etc/iptables.rules
++carol::iptables-restore < /etc/iptables.rules
++dave::iptables-restore < /etc/iptables.rules
++moon::rm /etc/swanctl/rsa/*
++carol::rm /etc/swanctl/rsa/*
++dave::rm /etc/swanctl/rsa/*
++moon::systemctl start strongswan
++carol::systemctl start strongswan
++dave::systemctl start strongswan
++moon::expect-connection rw
++carol::expect-connection home
++carol::swanctl --initiate --child home 2> /dev/null
++dave::expect-connection home
++dave::swanctl --initiate --child home 2> /dev/null
+diff --git a/testing/tests/botan/rw-mlkem/test.conf b/testing/tests/botan/rw-ml-kem-dsa/test.conf
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/test.conf
+rename to testing/tests/botan/rw-ml-kem-dsa/test.conf
+diff --git a/testing/tests/botan/rw-mlkem/description.txt b/testing/tests/botan/rw-ml-kem/description.txt
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/description.txt
+rename to testing/tests/botan/rw-ml-kem/description.txt
+diff --git a/testing/tests/wolfssl/rw-mlkem/evaltest.dat b/testing/tests/botan/rw-ml-kem/evaltest.dat
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/evaltest.dat
+rename to testing/tests/botan/rw-ml-kem/evaltest.dat
+diff --git a/testing/tests/botan/rw-mlkem/hosts/carol/etc/strongswan.conf b/testing/tests/botan/rw-ml-kem/hosts/carol/etc/strongswan.conf
+similarity index 95%
+rename from testing/tests/botan/rw-mlkem/hosts/carol/etc/strongswan.conf
+rename to testing/tests/botan/rw-ml-kem/hosts/carol/etc/strongswan.conf
+index 9230e2b009f..7a116eebeec 100755
+--- a/testing/tests/botan/rw-mlkem/hosts/carol/etc/strongswan.conf
++++ b/testing/tests/botan/rw-ml-kem/hosts/carol/etc/strongswan.conf
+@@ -1,6 +1,6 @@
+ # /etc/strongswan.conf - strongSwan configuration file
+ 
+- swanctl {
++swanctl {
+   load = botan pem x509 pubkey
+ }
+ 
+diff --git a/testing/tests/wolfssl/rw-mlkem/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/hosts/carol/etc/swanctl/swanctl.conf
+rename to testing/tests/botan/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/wolfssl/rw-mlkem/hosts/dave/etc/strongswan.conf b/testing/tests/botan/rw-ml-kem/hosts/dave/etc/strongswan.conf
+similarity index 95%
+rename from testing/tests/wolfssl/rw-mlkem/hosts/dave/etc/strongswan.conf
+rename to testing/tests/botan/rw-ml-kem/hosts/dave/etc/strongswan.conf
+index 3467fa3aca1..8b00dca7fb2 100755
+--- a/testing/tests/wolfssl/rw-mlkem/hosts/dave/etc/strongswan.conf
++++ b/testing/tests/botan/rw-ml-kem/hosts/dave/etc/strongswan.conf
+@@ -1,6 +1,6 @@
+ # /etc/strongswan.conf - strongSwan configuration file
+ 
+- swanctl {
++swanctl {
+   load = pem pkcs1 x509 pubkey openssl random
+ }
+ 
+diff --git a/testing/tests/wolfssl/rw-mlkem/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/hosts/dave/etc/swanctl/swanctl.conf
+rename to testing/tests/botan/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/botan/rw-mlkem/hosts/moon/etc/strongswan.conf b/testing/tests/botan/rw-ml-kem/hosts/moon/etc/strongswan.conf
+similarity index 95%
+rename from testing/tests/botan/rw-mlkem/hosts/moon/etc/strongswan.conf
+rename to testing/tests/botan/rw-ml-kem/hosts/moon/etc/strongswan.conf
+index 08034a46fe3..220a54dcc1f 100755
+--- a/testing/tests/botan/rw-mlkem/hosts/moon/etc/strongswan.conf
++++ b/testing/tests/botan/rw-ml-kem/hosts/moon/etc/strongswan.conf
+@@ -1,6 +1,6 @@
+ # /etc/strongswan.conf - strongSwan configuration file
+ 
+- swanctl {
++swanctl {
+   load = pem botan x509 pubkey
+ }
+ 
+diff --git a/testing/tests/wolfssl/rw-mlkem/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/botan/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/hosts/moon/etc/swanctl/swanctl.conf
+rename to testing/tests/botan/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/botan/rw-mlkem/posttest.dat b/testing/tests/botan/rw-ml-kem/posttest.dat
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/posttest.dat
+rename to testing/tests/botan/rw-ml-kem/posttest.dat
+diff --git a/testing/tests/botan/rw-mlkem/pretest.dat b/testing/tests/botan/rw-ml-kem/pretest.dat
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/pretest.dat
+rename to testing/tests/botan/rw-ml-kem/pretest.dat
+diff --git a/testing/tests/ikev2/rw-mlkem/test.conf b/testing/tests/botan/rw-ml-kem/test.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/test.conf
+rename to testing/tests/botan/rw-ml-kem/test.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem-dsa/description.txt b/testing/tests/ikev2/rw-ml-kem-dsa/description.txt
+new file mode 100755
+index 00000000000..bc7da0c8a7f
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem-dsa/description.txt
+@@ -0,0 +1,12 @@
++The roadwarriors <b>carol</b> and <b>dave</b> as well as the gateway <b>moon</b> use
++<b>openssl</b> plugin based on the <b>OpenSSL</b> library for all cryptographical
++functions plus the <b>ml</b> plugin for the post-quantum ML-KEM key exchange and
++ML-DSA signature algorithms.
++The authentication is based on <b>ML-DSA</b>-type X.509 certificates and the key
++exchange on <b>x25519-ke1_mlkem512</b> for <b>carol</b> and <b>ecp384-ke1_mlkem786</b>
++for <b>dave</b>.
++<p/>
++Upon the successful establishment of the IPsec tunnels, the updown script
++automatically inserts iptables-based firewall rules that let pass the tunneled traffic.
++In order to test both tunnel and firewall, both <b>carol</b> and <b>dave</b> ping
++the client <b>alice</b> behind the gateway <b>moon</b>.
+diff --git a/testing/tests/ikev2/rw-mlkem/evaltest.dat b/testing/tests/ikev2/rw-ml-kem-dsa/evaltest.dat
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/evaltest.dat
+rename to testing/tests/ikev2/rw-ml-kem-dsa/evaltest.dat
+diff --git a/testing/tests/ikev2/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf
+new file mode 100755
+index 00000000000..62f4836ceeb
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem pkcs8 ml x509 openssl pubkey
++}
++
++charon-systemd {
++  load = nonce pem pkcs8 ml x509 openssl revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/ikev2/rw-mlkem/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/hosts/carol/etc/swanctl/swanctl.conf
+rename to testing/tests/ikev2/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf
+new file mode 100755
+index 00000000000..62f4836ceeb
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem pkcs8 ml x509 openssl pubkey
++}
++
++charon-systemd {
++  load = nonce pem pkcs8 ml x509 openssl revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/ikev2/rw-mlkem/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/hosts/dave/etc/swanctl/swanctl.conf
+rename to testing/tests/ikev2/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf
+new file mode 100755
+index 00000000000..62f4836ceeb
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem pkcs8 ml x509 openssl pubkey
++}
++
++charon-systemd {
++  load = nonce pem pkcs8 ml x509 openssl revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/ikev2/rw-mlkem/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/ikev2/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/hosts/moon/etc/swanctl/swanctl.conf
+rename to testing/tests/ikev2/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem-dsa/posttest.dat b/testing/tests/ikev2/rw-ml-kem-dsa/posttest.dat
+new file mode 100755
+index 00000000000..f3b4dc9995c
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem-dsa/posttest.dat
+@@ -0,0 +1,11 @@
++carol::swanctl --terminate --ike home
++dave::swanctl --terminate --ike home
++carol::systemctl stop strongswan
++dave::systemctl stop strongswan
++moon::systemctl stop strongswan
++moon::rm /etc/swanctl/pkcs8/*
++carol::rm /etc/swanctl/pkcs8/*
++dave::rm /etc/swanctl/pkcs8/*
++moon::iptables-restore < /etc/iptables.flush
++carol::iptables-restore < /etc/iptables.flush
++dave::iptables-restore < /etc/iptables.flush
+diff --git a/testing/tests/ikev2/rw-ml-kem-dsa/pretest.dat b/testing/tests/ikev2/rw-ml-kem-dsa/pretest.dat
+new file mode 100755
+index 00000000000..85426cbb491
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem-dsa/pretest.dat
+@@ -0,0 +1,14 @@
++moon::iptables-restore < /etc/iptables.rules
++carol::iptables-restore < /etc/iptables.rules
++dave::iptables-restore < /etc/iptables.rules
++moon::rm /etc/swanctl/rsa/*
++carol::rm /etc/swanctl/rsa/*
++dave::rm /etc/swanctl/rsa/*
++moon::systemctl start strongswan
++carol::systemctl start strongswan
++dave::systemctl start strongswan
++moon::expect-connection rw
++carol::expect-connection home
++carol::swanctl --initiate --child home 2> /dev/null
++dave::expect-connection home
++dave::swanctl --initiate --child home 2> /dev/null
+diff --git a/testing/tests/wolfssl/rw-mlkem/test.conf b/testing/tests/ikev2/rw-ml-kem-dsa/test.conf
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/test.conf
+rename to testing/tests/ikev2/rw-ml-kem-dsa/test.conf
+diff --git a/testing/tests/ikev2/rw-mlkem/description.txt b/testing/tests/ikev2/rw-ml-kem/description.txt
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/description.txt
+rename to testing/tests/ikev2/rw-ml-kem/description.txt
+diff --git a/testing/tests/ikev2/rw-ml-kem/evaltest.dat b/testing/tests/ikev2/rw-ml-kem/evaltest.dat
+new file mode 100755
+index 00000000000..14a94d647cd
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem/evaltest.dat
+@@ -0,0 +1,10 @@
++carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=ML_KEM_512.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES
++dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=256 integ-alg=HMAC_SHA2_384_192 prf-alg=PRF_HMAC_SHA2_384 dh-group=ECP_384 ake1=ML_KEM_768.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=256.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES
++moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519 ake1=ML_KEM_512.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES
++moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=256 integ-alg=HMAC_SHA2_384_192 prf-alg=PRF_HMAC_SHA2_384 dh-group=ECP_384 ake1=ML_KEM_768.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=256.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES
++alice::ping -c 1 192.168.0.100::64 bytes from 192.168.0.100: icmp_.eq=1::YES
++alice::ping -c 1 192.168.0.200::64 bytes from 192.168.0.200: icmp_.eq=1::YES
++moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
++moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
++moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES
++moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES
+diff --git a/testing/tests/ikev2/rw-mlkem/hosts/carol/etc/strongswan.conf b/testing/tests/ikev2/rw-ml-kem/hosts/carol/etc/strongswan.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/hosts/carol/etc/strongswan.conf
+rename to testing/tests/ikev2/rw-ml-kem/hosts/carol/etc/strongswan.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/ikev2/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..0ddd655ef17
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf
+@@ -0,0 +1,35 @@
++connections {
++
++   home {
++      local_addrs  = 192.168.0.100
++      remote_addrs = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = carolCert.pem
++         id = carol@strongswan.org
++      }
++      remote {
++         auth = pubkey
++         id = moon.strongswan.org
++      }
++      children {
++         home {
++            remote_ts = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-x25519-ke1_mlkem512
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-x25519-ke1_mlkem512
++   }
++}
++
++secrets {
++
++   rsa-carol {
++      file = carolKey.pem
++      secret = "nH5ZQEWtku0RJEZ6"
++   }
++}
+diff --git a/testing/tests/ikev2/rw-mlkem/hosts/dave/etc/strongswan.conf b/testing/tests/ikev2/rw-ml-kem/hosts/dave/etc/strongswan.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/hosts/dave/etc/strongswan.conf
+rename to testing/tests/ikev2/rw-ml-kem/hosts/dave/etc/strongswan.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/ikev2/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..fa5b719fd35
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf
+@@ -0,0 +1,27 @@
++connections {
++
++   home {
++      local_addrs  = 192.168.0.200
++      remote_addrs = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = daveCert.pem
++         id = dave@strongswan.org
++      }
++      remote {
++         auth = pubkey
++         id = moon.strongswan.org
++      }
++      children {
++         home {
++            remote_ts = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes256gcm128-ecp384-ke1_mlkem768
++         }
++      }
++      version = 2
++      proposals = aes256-sha384-ecp384-ke1_mlkem768
++   }
++}
+diff --git a/testing/tests/ikev2/rw-mlkem/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/rw-ml-kem/hosts/moon/etc/strongswan.conf
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/hosts/moon/etc/strongswan.conf
+rename to testing/tests/ikev2/rw-ml-kem/hosts/moon/etc/strongswan.conf
+diff --git a/testing/tests/ikev2/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/ikev2/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..2730ba08a86
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf
+@@ -0,0 +1,25 @@
++connections {
++
++   rw {
++      local_addrs  = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = moonCert.pem
++         id = moon.strongswan.org
++      }
++      remote {
++         auth = pubkey
++      }
++      children {
++         net {
++            local_ts  = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes256gcm128-aes128gcm128-ecp384-x25519-ke1_mlkem768-ke1_mlkem1024-ke1_mlkem512-ke1_none
++         }
++      }
++      version = 2
++      proposals = aes256-aes128-sha384-sha256-ecp384-x25519-ke1_mlkem768-ke1_mlkem1024-ke1_mlkem512-ke1_none
++   }
++}
+diff --git a/testing/tests/ikev2/rw-mlkem/posttest.dat b/testing/tests/ikev2/rw-ml-kem/posttest.dat
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/posttest.dat
+rename to testing/tests/ikev2/rw-ml-kem/posttest.dat
+diff --git a/testing/tests/ikev2/rw-mlkem/pretest.dat b/testing/tests/ikev2/rw-ml-kem/pretest.dat
+similarity index 100%
+rename from testing/tests/ikev2/rw-mlkem/pretest.dat
+rename to testing/tests/ikev2/rw-ml-kem/pretest.dat
+diff --git a/testing/tests/ikev2/rw-ml-kem/test.conf b/testing/tests/ikev2/rw-ml-kem/test.conf
+new file mode 100755
+index 00000000000..1227b9d1c06
+--- /dev/null
++++ b/testing/tests/ikev2/rw-ml-kem/test.conf
+@@ -0,0 +1,25 @@
++#!/bin/bash
++#
++# This configuration file provides information on the
++# guest instances used for this test
++
++# All guest instances that are required for this test
++#
++VIRTHOSTS="alice moon carol winnetou dave"
++
++# Corresponding block diagram
++#
++DIAGRAM="a-m-c-w-d.png"
++
++# Guest instances on which tcpdump is to be started
++#
++TCPDUMPHOSTS="moon"
++
++# Guest instances on which IPsec is started
++# Used for IPsec logging purposes
++#
++IPSECHOSTS="moon carol dave"
++
++# charon controlled by swanctl
++#
++SWANCTL=1
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/description.txt b/testing/tests/wolfssl/rw-ml-kem-dsa/description.txt
+new file mode 100755
+index 00000000000..e052bdc511f
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/description.txt
+@@ -0,0 +1,11 @@
++The roadwarrior <b>carol</b> and the gateway <b>moon</b> use the <b>wolfssl</b>
++plugin based on the <b>wolfSSL</b> library for all cryptographical functions whereas
++roadwarrior <b>dave</b> uses the <b>openssl</b> and <b>ml</b> plugins. The
++authentication is based on <b>ML-DSA</b>-type X.509 certificates and the key
++exchange on <b>x25519-ke1_mlkem512</b> for <b>carol</b> and <b>ecp384-ke1_mlkem786</b>
++for <b>dave</b>.
++<p/>
++Upon the successful establishment of the IPsec tunnels, the updown script
++automatically inserts iptables-based firewall rules that let pass the tunneled traffic.
++In order to test both tunnel and firewall, both <b>carol</b> and <b>dave</b> ping
++the client <b>alice</b> behind the gateway <b>moon</b>.
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/evaltest.dat b/testing/tests/wolfssl/rw-ml-kem-dsa/evaltest.dat
+new file mode 100755
+index 00000000000..dec40736703
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/evaltest.dat
+@@ -0,0 +1,10 @@
++carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_384 ake1=ML_KEM_768.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES
++dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_521 ake1=ML_KEM_1024.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES
++moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_384 ake1=ML_KEM_768.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES
++moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_521 ake1=ML_KEM_1024.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES
++alice::ping -c 1 192.168.0.100::64 bytes from 192.168.0.100: icmp_.eq=1::YES
++alice::ping -c 1 192.168.0.200::64 bytes from 192.168.0.200: icmp_.eq=1::YES
++moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
++moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
++moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES
++moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf
+new file mode 100755
+index 00000000000..92a5f7bbc1e
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/carol/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem x509 wolfssl pubkey
++}
++
++charon-systemd {
++  load = nonce pem pkcs8 x509 wolfssl revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..248b57e9fd3
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/carol/etc/swanctl/swanctl.conf
+@@ -0,0 +1,35 @@
++connections {
++
++   home {
++      local_addrs  = 192.168.0.100
++      remote_addrs = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = carolCert.pem
++         id = carol@strongswan.org
++      }
++      remote {
++         auth = pubkey
++         id = moon.strongswan.org
++      }
++      children {
++         home {
++            remote_ts = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-ecp384-ke1_mlkem768
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-ecp384-ke1_mlkem768
++   }
++}
++
++secrets {
++
++   rsa-carol {
++      file = carolKey.pem
++      secret = "nH5ZQEWtku0RJEZ6"
++   }
++}
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf
+new file mode 100755
+index 00000000000..07d28859b39
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/dave/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem ml x509 pubkey openssl random
++}
++
++charon-systemd {
++  load = nonce pkcs8 ml x509 openssl pem revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..a06a2bfdd45
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/dave/etc/swanctl/swanctl.conf
+@@ -0,0 +1,27 @@
++connections {
++
++   home {
++      local_addrs  = 192.168.0.200
++      remote_addrs = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = daveCert.pem
++         id = dave@strongswan.org
++      }
++      remote {
++         auth = pubkey
++         id = moon.strongswan.org
++      }
++      children {
++         home {
++            remote_ts = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-ecp521-ke1_mlkem1024
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-ecp521-ke1_mlkem1024
++   }
++}
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf
+new file mode 100755
+index 00000000000..92a5f7bbc1e
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/moon/etc/strongswan.conf
+@@ -0,0 +1,12 @@
++# /etc/strongswan.conf - strongSwan configuration file
++
++swanctl {
++  load = pem x509 wolfssl pubkey
++}
++
++charon-systemd {
++  load = nonce pem pkcs8 x509 wolfssl revocation constraints pubkey curl kernel-netlink socket-default updown vici
++
++  retransmit_timeout = 4
++  max_packet = 12000
++}
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..a806a3661e1
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/hosts/moon/etc/swanctl/swanctl.conf
+@@ -0,0 +1,25 @@
++connections {
++
++   rw {
++      local_addrs  = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = moonCert.pem
++         id = moon.strongswan.org
++      }
++      remote {
++         auth = pubkey
++      }
++      children {
++         net {
++            local_ts  = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-ecp384-ecp521-ke1_mlkem768-ke1_mlkem1024-ke1_mlkem512-ke1_none
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-ecp384-ecp521-ke1_mlkem768-ke1_mlkem1024-ke1_mlkem512-ke1_none
++   }
++}
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/posttest.dat b/testing/tests/wolfssl/rw-ml-kem-dsa/posttest.dat
+new file mode 100755
+index 00000000000..f3b4dc9995c
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/posttest.dat
+@@ -0,0 +1,11 @@
++carol::swanctl --terminate --ike home
++dave::swanctl --terminate --ike home
++carol::systemctl stop strongswan
++dave::systemctl stop strongswan
++moon::systemctl stop strongswan
++moon::rm /etc/swanctl/pkcs8/*
++carol::rm /etc/swanctl/pkcs8/*
++dave::rm /etc/swanctl/pkcs8/*
++moon::iptables-restore < /etc/iptables.flush
++carol::iptables-restore < /etc/iptables.flush
++dave::iptables-restore < /etc/iptables.flush
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/pretest.dat b/testing/tests/wolfssl/rw-ml-kem-dsa/pretest.dat
+new file mode 100755
+index 00000000000..85426cbb491
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/pretest.dat
+@@ -0,0 +1,14 @@
++moon::iptables-restore < /etc/iptables.rules
++carol::iptables-restore < /etc/iptables.rules
++dave::iptables-restore < /etc/iptables.rules
++moon::rm /etc/swanctl/rsa/*
++carol::rm /etc/swanctl/rsa/*
++dave::rm /etc/swanctl/rsa/*
++moon::systemctl start strongswan
++carol::systemctl start strongswan
++dave::systemctl start strongswan
++moon::expect-connection rw
++carol::expect-connection home
++carol::swanctl --initiate --child home 2> /dev/null
++dave::expect-connection home
++dave::swanctl --initiate --child home 2> /dev/null
+diff --git a/testing/tests/wolfssl/rw-ml-kem-dsa/test.conf b/testing/tests/wolfssl/rw-ml-kem-dsa/test.conf
+new file mode 100755
+index 00000000000..1227b9d1c06
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem-dsa/test.conf
+@@ -0,0 +1,25 @@
++#!/bin/bash
++#
++# This configuration file provides information on the
++# guest instances used for this test
++
++# All guest instances that are required for this test
++#
++VIRTHOSTS="alice moon carol winnetou dave"
++
++# Corresponding block diagram
++#
++DIAGRAM="a-m-c-w-d.png"
++
++# Guest instances on which tcpdump is to be started
++#
++TCPDUMPHOSTS="moon"
++
++# Guest instances on which IPsec is started
++# Used for IPsec logging purposes
++#
++IPSECHOSTS="moon carol dave"
++
++# charon controlled by swanctl
++#
++SWANCTL=1
+diff --git a/testing/tests/wolfssl/rw-mlkem/description.txt b/testing/tests/wolfssl/rw-ml-kem/description.txt
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/description.txt
+rename to testing/tests/wolfssl/rw-ml-kem/description.txt
+diff --git a/testing/tests/wolfssl/rw-ml-kem/evaltest.dat b/testing/tests/wolfssl/rw-ml-kem/evaltest.dat
+new file mode 100755
+index 00000000000..dec40736703
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem/evaltest.dat
+@@ -0,0 +1,10 @@
++carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.100 local-port=4500 local-id=carol@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_384 ake1=ML_KEM_768.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.100/32] remote-ts=\[10.1.0.0/16]::YES
++dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED local-host=192.168.0.200 local-port=4500 local-id=dave@strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_521 ake1=ML_KEM_1024.*child-sas.*home.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[192.168.0.200/32] remote-ts=\[10.1.0.0/16]::YES
++moon:: swanctl --list-sas --ike-id 1 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_384 ake1=ML_KEM_768.*child-sas.*net.*reqid=1 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.100/32]::YES
++moon:: swanctl --list-sas --ike-id 2 --raw 2> /dev/null::rw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.200 remote-port=4500 remote-id=dave@strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=ECP_521 ake1=ML_KEM_1024.*child-sas.*net.*reqid=2 state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/16] remote-ts=\[192.168.0.200/32]::YES
++alice::ping -c 1 192.168.0.100::64 bytes from 192.168.0.100: icmp_.eq=1::YES
++alice::ping -c 1 192.168.0.200::64 bytes from 192.168.0.200: icmp_.eq=1::YES
++moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
++moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
++moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES
++moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES
+diff --git a/testing/tests/wolfssl/rw-mlkem/hosts/carol/etc/strongswan.conf b/testing/tests/wolfssl/rw-ml-kem/hosts/carol/etc/strongswan.conf
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/hosts/carol/etc/strongswan.conf
+rename to testing/tests/wolfssl/rw-ml-kem/hosts/carol/etc/strongswan.conf
+diff --git a/testing/tests/wolfssl/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/wolfssl/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..248b57e9fd3
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem/hosts/carol/etc/swanctl/swanctl.conf
+@@ -0,0 +1,35 @@
++connections {
++
++   home {
++      local_addrs  = 192.168.0.100
++      remote_addrs = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = carolCert.pem
++         id = carol@strongswan.org
++      }
++      remote {
++         auth = pubkey
++         id = moon.strongswan.org
++      }
++      children {
++         home {
++            remote_ts = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-ecp384-ke1_mlkem768
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-ecp384-ke1_mlkem768
++   }
++}
++
++secrets {
++
++   rsa-carol {
++      file = carolKey.pem
++      secret = "nH5ZQEWtku0RJEZ6"
++   }
++}
+diff --git a/testing/tests/botan/rw-mlkem/hosts/dave/etc/strongswan.conf b/testing/tests/wolfssl/rw-ml-kem/hosts/dave/etc/strongswan.conf
+similarity index 100%
+rename from testing/tests/botan/rw-mlkem/hosts/dave/etc/strongswan.conf
+rename to testing/tests/wolfssl/rw-ml-kem/hosts/dave/etc/strongswan.conf
+diff --git a/testing/tests/wolfssl/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf b/testing/tests/wolfssl/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..a06a2bfdd45
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem/hosts/dave/etc/swanctl/swanctl.conf
+@@ -0,0 +1,27 @@
++connections {
++
++   home {
++      local_addrs  = 192.168.0.200
++      remote_addrs = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = daveCert.pem
++         id = dave@strongswan.org
++      }
++      remote {
++         auth = pubkey
++         id = moon.strongswan.org
++      }
++      children {
++         home {
++            remote_ts = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-ecp521-ke1_mlkem1024
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-ecp521-ke1_mlkem1024
++   }
++}
+diff --git a/testing/tests/wolfssl/rw-mlkem/hosts/moon/etc/strongswan.conf b/testing/tests/wolfssl/rw-ml-kem/hosts/moon/etc/strongswan.conf
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/hosts/moon/etc/strongswan.conf
+rename to testing/tests/wolfssl/rw-ml-kem/hosts/moon/etc/strongswan.conf
+diff --git a/testing/tests/wolfssl/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf b/testing/tests/wolfssl/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf
+new file mode 100755
+index 00000000000..a806a3661e1
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem/hosts/moon/etc/swanctl/swanctl.conf
+@@ -0,0 +1,25 @@
++connections {
++
++   rw {
++      local_addrs  = 192.168.0.1
++
++      local {
++         auth = pubkey
++         certs = moonCert.pem
++         id = moon.strongswan.org
++      }
++      remote {
++         auth = pubkey
++      }
++      children {
++         net {
++            local_ts  = 10.1.0.0/16
++
++            updown = /usr/local/libexec/ipsec/_updown iptables
++            esp_proposals = aes128gcm128-ecp384-ecp521-ke1_mlkem768-ke1_mlkem1024-ke1_mlkem512-ke1_none
++         }
++      }
++      version = 2
++      proposals = aes128-sha256-ecp384-ecp521-ke1_mlkem768-ke1_mlkem1024-ke1_mlkem512-ke1_none
++   }
++}
+diff --git a/testing/tests/wolfssl/rw-mlkem/posttest.dat b/testing/tests/wolfssl/rw-ml-kem/posttest.dat
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/posttest.dat
+rename to testing/tests/wolfssl/rw-ml-kem/posttest.dat
+diff --git a/testing/tests/wolfssl/rw-mlkem/pretest.dat b/testing/tests/wolfssl/rw-ml-kem/pretest.dat
+similarity index 100%
+rename from testing/tests/wolfssl/rw-mlkem/pretest.dat
+rename to testing/tests/wolfssl/rw-ml-kem/pretest.dat
+diff --git a/testing/tests/wolfssl/rw-ml-kem/test.conf b/testing/tests/wolfssl/rw-ml-kem/test.conf
+new file mode 100755
+index 00000000000..1227b9d1c06
+--- /dev/null
++++ b/testing/tests/wolfssl/rw-ml-kem/test.conf
+@@ -0,0 +1,25 @@
++#!/bin/bash
++#
++# This configuration file provides information on the
++# guest instances used for this test
++
++# All guest instances that are required for this test
++#
++VIRTHOSTS="alice moon carol winnetou dave"
++
++# Corresponding block diagram
++#
++DIAGRAM="a-m-c-w-d.png"
++
++# Guest instances on which tcpdump is to be started
++#
++TCPDUMPHOSTS="moon"
++
++# Guest instances on which IPsec is started
++# Used for IPsec logging purposes
++#
++IPSECHOSTS="moon carol dave"
++
++# charon controlled by swanctl
++#
++SWANCTL=1
+diff --git a/testing/tests/wolfssl/rw-modp3072/hosts/carol/etc/strongswan.conf b/testing/tests/wolfssl/rw-modp3072/hosts/carol/etc/strongswan.conf
+index 6af47fb53b9..db186d53aea 100755
+--- a/testing/tests/wolfssl/rw-modp3072/hosts/carol/etc/strongswan.conf
++++ b/testing/tests/wolfssl/rw-modp3072/hosts/carol/etc/strongswan.conf
+@@ -1,11 +1,11 @@
+ # /etc/strongswan.conf - strongSwan configuration file
+ 
+  swanctl {
+-  load = pem wolfssl pkcs1 x509 pubkey
++  load = pem pkcs1 wolfssl x509 pubkey
+ }
+ 
+ charon-systemd {
+-  load = nonce pem wolfssl pkcs1 x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici
++  load = nonce pem pkcs1 pkcs8 wolfssl x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici
+ 
+   rsa_pss = yes
+ }
+diff --git a/testing/tests/wolfssl/rw-modp3072/hosts/dave/etc/strongswan.conf b/testing/tests/wolfssl/rw-modp3072/hosts/dave/etc/strongswan.conf
+index 70f12a9ca58..f80ccd3abf9 100755
+--- a/testing/tests/wolfssl/rw-modp3072/hosts/dave/etc/strongswan.conf
++++ b/testing/tests/wolfssl/rw-modp3072/hosts/dave/etc/strongswan.conf
+@@ -1,11 +1,11 @@
+ # /etc/strongswan.conf - strongSwan configuration file
+ 
+  swanctl {
+-  load = pem pkcs1 pubkey openssl random
++  load = pem pubkey openssl random
+ }
+ 
+ charon-systemd {
+-  load = random nonce openssl pem pkcs1 revocation constraints pubkey curl kernel-netlink socket-default updown vici
++  load = random nonce openssl pem revocation constraints pubkey curl kernel-netlink socket-default updown vici
+ 
+   rsa_pss = yes
+ }
+diff --git a/testing/tests/wolfssl/rw-modp3072/hosts/moon/etc/strongswan.conf b/testing/tests/wolfssl/rw-modp3072/hosts/moon/etc/strongswan.conf
+index dfad35cbb07..5200b4da696 100755
+--- a/testing/tests/wolfssl/rw-modp3072/hosts/moon/etc/strongswan.conf
++++ b/testing/tests/wolfssl/rw-modp3072/hosts/moon/etc/strongswan.conf
+@@ -1,11 +1,11 @@
+ # /etc/strongswan.conf - strongSwan configuration file
+ 
+  swanctl {
+-  load = pem wolfssl pkcs1 x509 pubkey
++  load = pem pkcs1 wolfssl x509 pubkey
+ }
+ 
+ charon-systemd {
+-  load = nonce test-vectors pem wolfssl pkcs1 x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici
++  load = nonce test-vectors pem pkcs1 wolfssl x509 revocation constraints pubkey curl kernel-netlink socket-default updown vici
+ 
+   rsa_pss = yes
+ }
+
+diff --git a/src/libstrongswan/plugins/openssl/Makefile.am b/src/libstrongswan/plugins/openssl/Makefile.am
+index 636851a8eae..30edffa0004 100644
+--- a/src/libstrongswan/plugins/openssl/Makefile.am
++++ b/src/libstrongswan/plugins/openssl/Makefile.am
+@@ -39,6 +39,8 @@ libstrongswan_openssl_la_SOURCES = \
+ 	openssl_x_diffie_hellman.c openssl_x_diffie_hellman.h \
+ 	openssl_ed_private_key.c openssl_ed_private_key.h \
+ 	openssl_ed_public_key.c openssl_ed_public_key.h \
++	openssl_ml_dsa_private_key.c openssl_ml_dsa_private_key.h \
++	openssl_ml_dsa_public_key.c openssl_ml_dsa_public_key.h \
+ 	openssl_xof.c openssl_xof.h \
+ 	openssl_kem.c openssl_kem.h
+ 
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.c
+new file mode 100644
+index 00000000000..f777fd5ff1d
+--- /dev/null
++++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.c
+@@ -0,0 +1,383 @@
++/*
++ * Copyright (C) 2025 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include <openssl/evp.h>
++
++#if OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA)
++#include <openssl/core_names.h>
++#include <openssl/params.h>
++#include <openssl/provider.h>
++
++#include "openssl_ml_dsa_private_key.h"
++#include "openssl_util.h"
++
++#include <utils/debug.h>
++
++typedef struct private_private_key_t private_private_key_t;
++
++#define ML_DSA_SEED_LEN 32
++
++/**
++ * Private data
++ */
++struct private_private_key_t {
++
++	/**
++	 * Public interface
++	 */
++	private_key_t public;
++
++	/**
++	 * Key object
++	 */
++	EVP_PKEY *key;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * reference count
++	 */
++	refcount_t ref;
++};
++
++/* from openssl_ml_dsa public key */
++char *openssl_ml_dsa_alg_name(key_type_t type);
++key_type_t openssl_ml_dsa_evp_pkey_key_type(EVP_PKEY *key);
++bool openssl_ml_dsa_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp);
++
++METHOD(private_key_t, sign, bool,
++	private_private_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t *signature)
++{
++	pqc_params_t pqc_params;
++	OSSL_PARAM ossl_params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END};
++	EVP_PKEY_CTX *ctx = NULL;
++	EVP_SIGNATURE *sig_alg = NULL;
++	int deterministic;
++	bool success = FALSE;
++
++	if ((this->type == KEY_ML_DSA_44 && scheme != SIGN_ML_DSA_44) ||
++		(this->type == KEY_ML_DSA_65 && scheme != SIGN_ML_DSA_65) ||
++		(this->type == KEY_ML_DSA_87 && scheme != SIGN_ML_DSA_87))
++	{
++		DBG1(DBG_LIB, "signature scheme %N not supported by %N key",
++			 signature_scheme_names, scheme, key_type_names, this->type);
++		return FALSE;
++	}
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		return FALSE;
++	}
++	deterministic = pqc_params.deterministic ? 1 : 0;
++	ossl_params[0] = OSSL_PARAM_construct_int(
++									OSSL_SIGNATURE_PARAM_DETERMINISTIC,
++									&deterministic);
++	if (pqc_params.ctx.len)
++	{
++		ossl_params[1] = OSSL_PARAM_construct_octet_string(
++									OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
++									pqc_params.ctx.ptr, pqc_params.ctx.len);
++	}
++
++	ctx = EVP_PKEY_CTX_new_from_pkey(NULL, this->key, NULL);
++	if (!ctx)
++	{
++		goto error;
++	}
++	sig_alg = EVP_SIGNATURE_fetch(NULL, openssl_ml_dsa_alg_name(this->type), NULL);
++
++	if (EVP_PKEY_sign_message_init(ctx, sig_alg, ossl_params) <= 0)
++	{
++		goto error;
++	}
++
++	if (EVP_PKEY_sign(ctx, NULL, &signature->len, data.ptr, data.len) <= 0)
++	{
++		goto error;
++	}
++
++	*signature = chunk_alloc(signature->len);
++
++	if (EVP_PKEY_sign(ctx, signature->ptr, &signature->len,
++	                  data.ptr, data.len) <= 0)
++	{
++		goto error;
++	}
++	success = TRUE;
++
++error:
++	pqc_params_free(&pqc_params);
++	EVP_SIGNATURE_free(sig_alg);
++	EVP_PKEY_CTX_free(ctx);
++
++	return success;
++}
++
++METHOD(private_key_t, decrypt, bool,
++	private_private_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "ML-DSA private key decryption not implemented");
++	return FALSE;
++}
++
++METHOD(private_key_t, get_keysize, int,
++	private_private_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++METHOD(private_key_t, get_type, key_type_t,
++	private_private_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(private_key_t, get_public_key, public_key_t*,
++	private_private_key_t *this)
++{
++	public_key_t *public;
++	u_char buf[2592];
++	chunk_t key = {buf, sizeof(buf)};
++
++	if (!EVP_PKEY_get_octet_string_param(this->key, OSSL_PKEY_PARAM_PUB_KEY,
++										 buf, sizeof(buf), &key.len))
++	{
++		return NULL;
++	}
++	public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, this->type,
++								BUILD_BLOB, key, BUILD_END);
++	return public;
++}
++
++METHOD(private_key_t, get_fingerprint, bool,
++	private_private_key_t *this, cred_encoding_type_t type,
++	chunk_t *fingerprint)
++{
++	return openssl_ml_dsa_fingerprint(this->key, type, fingerprint);
++}
++
++METHOD(private_key_t, get_encoding, bool,
++	private_private_key_t *this, cred_encoding_type_t type, chunk_t *encoding)
++{
++	switch (type)
++	{
++		case PRIVKEY_ASN1_DER:
++		case PRIVKEY_PEM:
++		{
++			bool success = TRUE;
++
++			*encoding = openssl_i2chunk(PrivateKey, this->key);
++
++			if (type == PRIVKEY_PEM)
++			{
++				chunk_t asn1_encoding = *encoding;
++
++				success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM,
++								NULL, encoding, CRED_PART_PRIV_ASN1_DER,
++								asn1_encoding, CRED_PART_END);
++				chunk_clear(&asn1_encoding);
++			}
++
++			return success;
++		}
++		default:
++			return FALSE;
++	}
++}
++
++METHOD(private_key_t, get_ref, private_key_t*,
++	private_private_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(private_key_t, destroy, void,
++	private_private_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this->key);
++		EVP_PKEY_free(this->key);
++		free(this);
++	}
++}
++
++/**
++ * Internal generic constructor
++ */
++static private_key_t *create_internal(key_type_t type, EVP_PKEY *key)
++{
++	private_private_key_t *this;
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.sign = _sign,
++			.decrypt = _decrypt,
++			.get_keysize = _get_keysize,
++			.get_public_key = _get_public_key,
++			.equals = private_key_equals,
++			.belongs_to = private_key_belongs_to,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = private_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.key = key,
++		.ref = 1,
++	);
++
++	return &this->public;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *openssl_ml_dsa_private_key_create(EVP_PKEY *key, bool engine)
++{
++	key_type_t type;
++
++	type = openssl_ml_dsa_evp_pkey_key_type(key);
++	if (type == KEY_ANY)
++	{
++		EVP_PKEY_free(key);
++		return NULL;
++	}
++	return create_internal(type, key);
++}
++
++/**
++ * Create an ML-DSA private_key_t instance from a seed
++ */
++static private_key_t *create_instance(key_type_t type, chunk_t keyseed)
++{
++	EVP_PKEY_CTX *ctx = NULL;
++	EVP_PKEY *key = NULL;
++
++	ctx = EVP_PKEY_CTX_new_from_name(NULL, openssl_ml_dsa_alg_name(type), NULL);
++	if (!ctx || EVP_PKEY_keygen_init(ctx) <= 0)
++	{
++		DBG1(DBG_LIB, "failed to create ctx");
++		goto end;
++	}
++
++	if (keyseed.ptr)
++	{
++		OSSL_PARAM params[] = {
++			OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED,
++									keyseed.ptr, keyseed.len),
++			OSSL_PARAM_END
++		};
++
++		if (!EVP_PKEY_CTX_set_params(ctx, params))
++		{
++			DBG1(DBG_LIB, "failed to set keyseed");
++			goto end;
++		}
++	}
++
++	if (EVP_PKEY_generate(ctx, &key) <= 0)
++	{
++		DBG1(DBG_LIB, "failed to generate ML-DSA private key");
++		goto end;
++	}
++
++end:
++	EVP_PKEY_CTX_free(ctx);
++	return key ? create_internal(type, key) : NULL;
++}
++
++/*
++ * Described in header
++ */
++private_key_t *openssl_ml_dsa_private_key_gen(key_type_t type, va_list args)
++{
++	if (type != KEY_ML_DSA_44 && type != KEY_ML_DSA_65 && type != KEY_ML_DSA_87)
++	{
++		return NULL;
++	}
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_KEY_SIZE:
++				/* just ignore the key size */
++				va_arg(args, u_int);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	return create_instance(type, chunk_empty);
++}
++
++/*
++ * Described in header
++ */
++private_key_t *openssl_ml_dsa_private_key_load(key_type_t type, va_list args)
++{
++	chunk_t priv = chunk_empty;
++
++	if (type != KEY_ML_DSA_44 && type != KEY_ML_DSA_65 && type != KEY_ML_DSA_87)
++	{
++		return NULL;
++	}
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				priv = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (priv.len == ML_DSA_SEED_LEN + 2 &&
++		priv.ptr[0] == 0x80 && priv.ptr[1] == ML_DSA_SEED_LEN)
++	{
++		priv = chunk_skip(priv, 2);
++	}
++	if (priv.len != ML_DSA_SEED_LEN)
++	{
++		DBG1(DBG_LIB, "error: the size of the loaded ML-DSA private key seed "
++			 "is %u bytes instead of %d bytes", priv.len, ML_DSA_SEED_LEN);
++		return NULL;
++	}
++
++	return create_instance(type, priv);
++}
++
++
++#endif /* OPENSSL_NO_ML_DSA*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.h b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.h
+new file mode 100644
+index 00000000000..2aae835a0dd
+--- /dev/null
++++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_private_key.h
+@@ -0,0 +1,59 @@
++/*
++ * Copyright (C) 2025 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup openssl_ml_dsa_private_key openssl_ml_dsa_private_key
++ * @{ @ingroup openssl_p
++ */
++
++#ifndef OPENSSL_ML_DSA_PRIVATE_KEY_H_
++#define OPENSSL_ML_DSA_PRIVATE_KEY_H_
++
++#include <openssl/evp.h>
++
++#include <credentials/builder.h>
++#include <credentials/keys/private_key.h>
++
++/**
++ * Generate an ML-DSA private key using OpenSSL.
++ *
++ * @param type		type of the key, must be ML_DSA_44, ML_DSA_65 or ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			generated key, NULL on failure
++ */
++private_key_t *openssl_ml_dsa_private_key_gen(key_type_t type, va_list args);
++
++/**
++ * Load an ML-DSA private key using OpenSSL.
++ *
++ * Accepts a BUILD_BLOB_ASN1_DER argument.
++ *
++ * @param type		type of the key, must be ML_DSA_44, ML_DSA_65 or ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++private_key_t *openssl_ml_dsa_private_key_load(key_type_t type, va_list args);
++
++/**
++ * Wrap an EVP_PKEY object of type ML-DSA-44/64/87.
++ *
++ * @param key		EVP_PKEY object (adopted)
++ * @param engine	whether the key was loaded via an engine
++ * @return 			generated key, NULL on failure
++ */
++private_key_t *openssl_ml_dsa_private_key_create(EVP_PKEY *key, bool engine);
++
++#endif /** OPENSSL_ML_DSA_PRIVATE_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
+new file mode 100644
+index 00000000000..c1263da3c4a
+--- /dev/null
++++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
+@@ -0,0 +1,362 @@
++/*
++ * Copyright (C) 2025 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++#include <openssl/evp.h>
++
++#if OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA)
++#include <openssl/x509.h>
++#include <openssl/core_names.h>
++#include <openssl/params.h>
++
++#include "openssl_ml_dsa_public_key.h"
++
++#include <utils/debug.h>
++
++typedef struct private_public_key_t private_public_key_t;
++
++/**
++ * Private data
++ */
++struct private_public_key_t {
++
++	/**
++	 * Public interface
++	 */
++	public_key_t public;
++
++	/**
++	 * Key object
++	 */
++	EVP_PKEY *key;
++
++	/**
++	 * Key type
++	 */
++	key_type_t type;
++
++	/**
++	 * Reference counter
++	 */
++	refcount_t ref;
++};
++
++/**
++ * Map a key type to an algorithm name (also used by private key)
++ */
++char *openssl_ml_dsa_alg_name(key_type_t type)
++{
++	switch (type)
++	{
++		case KEY_ML_DSA_44:
++			return "ML-DSA-44";
++		case KEY_ML_DSA_65:
++			return "ML-DSA-65";
++		case KEY_ML_DSA_87:
++			return "ML-DSA-87";
++		default:
++			return NULL;
++	}
++}
++
++/**
++ * Determine the ML-DSA key type for the given key (also used by private key)
++ */
++key_type_t openssl_ml_dsa_evp_pkey_key_type(EVP_PKEY *key)
++{
++	if (EVP_PKEY_is_a(key, "ML-DSA-44"))
++	{
++		return KEY_ML_DSA_44;
++	}
++	else if (EVP_PKEY_is_a(key, "ML-DSA-65"))
++	{
++		return KEY_ML_DSA_65;
++	}
++	else if (EVP_PKEY_is_a(key, "ML-DSA-87"))
++	{
++		return KEY_ML_DSA_87;
++	}
++	return KEY_ANY;
++}
++
++/**
++ * Map a key type to an EVP key type
++ */
++static int openssl_ml_dsa_key_type(key_type_t type)
++{
++	switch (type)
++	{
++		case KEY_ML_DSA_44:
++			return EVP_PKEY_ML_DSA_44;
++		case KEY_ML_DSA_65:
++			return EVP_PKEY_ML_DSA_65;
++		case KEY_ML_DSA_87:
++			return EVP_PKEY_ML_DSA_87;
++		default:
++			return 0;
++	}
++}
++
++METHOD(public_key_t, get_type, key_type_t,
++	private_public_key_t *this)
++{
++	return this->type;
++}
++
++METHOD(public_key_t, verify, bool,
++	private_public_key_t *this, signature_scheme_t scheme,
++	void *params, chunk_t data, chunk_t signature)
++{
++	pqc_params_t pqc_params;
++	EVP_PKEY_CTX *ctx = NULL;
++	EVP_SIGNATURE *sig_alg = NULL;
++	OSSL_PARAM ossl_params[] = { OSSL_PARAM_END, OSSL_PARAM_END};
++	bool success = FALSE;
++
++	if ((this->type == KEY_ML_DSA_44 && scheme != SIGN_ML_DSA_44) ||
++		(this->type == KEY_ML_DSA_65 && scheme != SIGN_ML_DSA_65) ||
++		(this->type == KEY_ML_DSA_87 && scheme != SIGN_ML_DSA_87))
++	{
++		DBG1(DBG_LIB, "signature scheme %N not supported by %N key",
++			 signature_scheme_names, scheme, key_type_names, this->type);
++		return FALSE;
++	}
++
++	/* set PQC signature params */
++	if (!pqc_params_create(params, &pqc_params))
++	{
++		return FALSE;
++	}
++	if (pqc_params.ctx.len)
++	{
++		ossl_params[0] = OSSL_PARAM_construct_octet_string(
++									OSSL_SIGNATURE_PARAM_CONTEXT_STRING,
++									pqc_params.ctx.ptr, pqc_params.ctx.len);
++	}
++
++	ctx = EVP_PKEY_CTX_new_from_pkey(NULL, this->key, NULL);
++	if (!ctx)
++	{
++		goto error;
++	}
++	sig_alg = EVP_SIGNATURE_fetch(NULL, openssl_ml_dsa_alg_name(this->type), NULL);
++
++	if (EVP_PKEY_verify_message_init(ctx, sig_alg, ossl_params) <= 0)
++	{
++		goto error;
++	}
++
++	if (EVP_PKEY_verify(ctx, signature.ptr, signature.len, data.ptr, data.len) <= 0)
++	{
++		goto error;
++	}
++	success = TRUE;
++
++error:
++	pqc_params_free(&pqc_params);
++	EVP_SIGNATURE_free(sig_alg);
++	EVP_PKEY_CTX_free(ctx);
++
++	return success;
++}
++
++METHOD(public_key_t, encrypt, bool,
++	private_public_key_t *this, encryption_scheme_t scheme,
++	void *params, chunk_t crypto, chunk_t *plain)
++{
++	DBG1(DBG_LIB, "encryption scheme %N not supported", encryption_scheme_names,
++		 scheme);
++	return FALSE;
++}
++
++METHOD(public_key_t, get_keysize, int,
++	private_public_key_t *this)
++{
++	return BITS_PER_BYTE * get_public_key_size(this->type);
++}
++
++/**
++ * Calculate fingerprint from an EdDSA key, also used in ed private key.
++ */
++bool openssl_ml_dsa_fingerprint(EVP_PKEY *key, cred_encoding_type_t type,
++							chunk_t *fp)
++{
++	hasher_t *hasher;
++	chunk_t blob;
++	u_char *p;
++
++	if (lib->encoding->get_cache(lib->encoding, type, key, fp))
++	{
++		return TRUE;
++	}
++	switch (type)
++	{
++		case KEYID_PUBKEY_SHA1:
++			if (!EVP_PKEY_get_raw_public_key(key, NULL, &blob.len))
++			{
++				return FALSE;
++			}
++			blob = chunk_alloca(blob.len);
++			if (!EVP_PKEY_get_raw_public_key(key, blob.ptr, &blob.len))
++			{
++				return FALSE;
++			}
++			break;
++		case KEYID_PUBKEY_INFO_SHA1:
++			blob = chunk_alloca(i2d_PUBKEY(key, NULL));
++			p = blob.ptr;
++			i2d_PUBKEY(key, &p);
++			break;
++		default:
++			return FALSE;
++	}
++	hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
++	if (!hasher || !hasher->allocate_hash(hasher, blob, fp))
++	{
++		DBG1(DBG_LIB, "SHA1 not supported, fingerprinting failed");
++		DESTROY_IF(hasher);
++		return FALSE;
++	}
++	hasher->destroy(hasher);
++	lib->encoding->cache(lib->encoding, type, key, fp);
++	return TRUE;
++}
++
++METHOD(public_key_t, get_fingerprint, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *fingerprint)
++{
++	return openssl_ml_dsa_fingerprint(this->key, type, fingerprint);
++}
++
++METHOD(public_key_t, get_encoding, bool,
++	private_public_key_t *this, cred_encoding_type_t type, chunk_t *encoding)
++{
++	bool success = TRUE;
++	u_char *p;
++
++	*encoding = chunk_alloc(i2d_PUBKEY(this->key, NULL));
++	p = encoding->ptr;
++	i2d_PUBKEY(this->key, &p);
++
++	if (type != PUBKEY_SPKI_ASN1_DER)
++	{
++		chunk_t asn1_encoding = *encoding;
++
++		success = lib->encoding->encode(lib->encoding, type,
++								NULL, encoding, CRED_PART_PUB_ASN1_DER,
++								asn1_encoding, CRED_PART_END);
++		chunk_clear(&asn1_encoding);
++	}
++	return success;
++}
++
++METHOD(public_key_t, get_ref, public_key_t*,
++	private_public_key_t *this)
++{
++	ref_get(&this->ref);
++	return &this->public;
++}
++
++METHOD(public_key_t, destroy, void,
++	private_public_key_t *this)
++{
++	if (ref_put(&this->ref))
++	{
++		lib->encoding->clear_cache(lib->encoding, this->key);
++		EVP_PKEY_free(this->key);
++		free(this);
++	}
++}
++
++/**
++ * Generic private constructor
++ */
++static private_public_key_t *create_empty(key_type_t type)
++{
++	private_public_key_t *this;
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.verify = _verify,
++			.encrypt = _encrypt,
++			.get_keysize = _get_keysize,
++			.equals = public_key_equals,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = public_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.type = type,
++		.ref = 1,
++	);
++
++	return this;
++}
++
++/*
++ * Described in header
++ */
++public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args)
++{
++	private_public_key_t *this;
++	chunk_t pkcs1, blob = chunk_empty;
++	EVP_PKEY *key = NULL;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB:
++				blob = va_arg(args, chunk_t);
++				continue;
++			case BUILD_BLOB_ASN1_DER:
++				pkcs1 = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++
++	if (blob.len)
++	{
++		key = EVP_PKEY_new_raw_public_key(openssl_ml_dsa_key_type(type), NULL,
++										  blob.ptr, blob.len);
++	}
++	else if (pkcs1.len)
++	{
++		key = d2i_PUBKEY(NULL, (const u_char**)&pkcs1.ptr, pkcs1.len);
++		type = openssl_ml_dsa_evp_pkey_key_type(key);
++		if (key && type == KEY_ANY)
++		{
++			EVP_PKEY_free(key);
++			return NULL;
++		}
++	}
++	if (!key)
++	{
++		return NULL;
++	}
++	this = create_empty(type);
++	this->key = key;
++
++	return &this->public;
++}
++
++#endif /* OPENSSL_VERSION_NUMBER */
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
+new file mode 100644
+index 00000000000..223830c5af5
+--- /dev/null
++++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2025 Andreas Steffen
++ *
++ * Copyright (C) secunet Security Networks AG
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ */
++
++/**
++ * @defgroup openssl_ml_dsa_public_key openssl_ml_dsa_public_key
++ * @{ @ingroup openssl_p
++ */
++
++#ifndef OPENSSL_ML_DSA_PUBLIC_KEY_H_
++#define OPENSSL_ML_DSA_PUBLIC_KEY_H_
++
++#include <credentials/builder.h>
++#include <credentials/keys/public_key.h>
++
++/**
++ * Load an ML-DSA public key using OpenSSL.
++ *
++ * Accepts a BUILD_BLOB_ASN1_DER argument.
++ *
++ * @param type		type of the key, must be ML_DSA_44, ML_DSA_65 or ML_DSA_87
++ * @param args		builder_part_t argument list
++ * @return 			loaded key, NULL on failure
++ */
++public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args);
++
++#endif /** OPENSSL_ML_DSA_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c
+index a12e2ba8fc9..1dea3b60188 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_plugin.c
++++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c
+@@ -1,6 +1,7 @@
+ /*
+  * Copyright (C) 2008-2020 Tobias Brunner
+  * Copyright (C) 2008 Martin Willi
++ * Copyright (C) 2025 Andreas Steffen
+  *
+  * Copyright (C) secunet Security Networks AG
+  *
+@@ -31,6 +32,7 @@
+ #endif
+ #if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ #include <openssl/provider.h>
++#include <openssl/core_names.h>
+ #endif
+ 
+ #include "openssl_plugin.h"
+@@ -56,6 +58,8 @@
+ #include "openssl_x_diffie_hellman.h"
+ #include "openssl_ed_public_key.h"
+ #include "openssl_ed_private_key.h"
++#include "openssl_ml_dsa_private_key.h"
++#include "openssl_ml_dsa_public_key.h"
+ #include "openssl_xof.h"
+ #include "openssl_kem.h"
+ 
+@@ -286,7 +290,6 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args)
+ 		}
+ 		break;
+ 	}
+-
+ 	if (blob.ptr)
+ 	{
+ 		key = d2i_AutoPrivateKey(NULL, (const u_char**)&blob.ptr, blob.len);
+@@ -669,6 +672,29 @@ METHOD(plugin_t, get_features, int,
+ 		PLUGIN_REGISTER(HASHER, return_null),
+ 			PLUGIN_PROVIDE(HASHER, HASH_IDENTITY),
+ #endif /* OPENSSL_VERSION_NUMBER && !OPENSSL_NO_EC && !OPENSSL_IS_AWSLC */
++#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA))
++		/* ML-DSA private/public key loading */
++		PLUGIN_REGISTER(PUBKEY, openssl_ml_dsa_public_key_load, TRUE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
++		PLUGIN_REGISTER(PRIVKEY, openssl_ml_dsa_private_key_load, TRUE),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_87),
++		PLUGIN_REGISTER(PRIVKEY_GEN, openssl_ml_dsa_private_key_gen, FALSE),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_44),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_65),
++			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_ML_DSA_87),
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_44),
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_65),
++		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_ML_DSA_87),
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_44),
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_65),
++		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_ML_DSA_87),
++#endif /* OPENSSL_NO_ML_DSA */
++
+ 		/* generic key loader */
+ 		PLUGIN_REGISTER(PRIVKEY, openssl_private_key_load, TRUE),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
+@@ -769,6 +795,30 @@ static int concat_ossl_providers(OSSL_PROVIDER *provider, void *cbdata)
+ }
+ #endif
+ 
++#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA))
++/**
++ * Callback to add ml.dsa parameters to loaded providers
++ */
++static int add_ml_dsa_ossl_params(OSSL_PROVIDER *provider, void *cbdata)
++{
++	/* prefer seed-only format but allow priv-only if we loaded a priv-only
++	 * key and need to re-encode it again (e.g. in swanctl after decrypting
++	 * a PKCS#8 file) */
++	if (!OSSL_PROVIDER_add_conf_parameter(provider,
++						OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, "yes") ||
++		!OSSL_PROVIDER_add_conf_parameter(provider,
++						OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, "yes") ||
++		!OSSL_PROVIDER_add_conf_parameter(provider,
++						OSSL_PKEY_PARAM_ML_DSA_OUTPUT_FORMATS,
++						"seed-only priv-only"))
++	{
++		DBG1(DBG_LIB, "failed to set ml.dsa parameters in '%s' provider",
++			 OSSL_PROVIDER_get0_name(provider));
++	}
++	return 1;
++}
++#endif /* OPENSSL_NO_ML_DSA */
++
+ /*
+  * see header file
+  */
+@@ -856,6 +906,9 @@ PLUGIN_DEFINE(openssl)
+ 	OSSL_PROVIDER_do_all(NULL, concat_ossl_providers, &data);
+ 	dbg(DBG_LIB, strpfx(lib->ns, "charon") ? 1 : 2,
+ 		"providers loaded by OpenSSL:%s", data.names);
++#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA))
++	OSSL_PROVIDER_do_all(NULL, add_ml_dsa_ossl_params, NULL);
++#endif /* OPENSSL_NO_ML_DSA */
+ #endif /* OPENSSL_VERSION_NUMBER */
+ 
+ #ifdef OPENSSL_FIPS
+diff --git a/src/libstrongswan/plugins/openssl/openssl_util.c b/src/libstrongswan/plugins/openssl/openssl_util.c
+index ae72710fb3c..10027b37feb 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_util.c
++++ b/src/libstrongswan/plugins/openssl/openssl_util.c
+@@ -31,6 +31,7 @@
+ #include "openssl_ec_private_key.h"
+ #include "openssl_ed_private_key.h"
+ #include "openssl_rsa_private_key.h"
++#include "openssl_ml_dsa_private_key.h"
+ 
+ /* these were added with 1.1.0 when ASN1_OBJECT was made opaque */
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
+@@ -160,6 +161,16 @@ private_key_t *openssl_wrap_private_key(EVP_PKEY *key, bool engine)
+ 				return openssl_ed_private_key_create(key, engine);
+ #endif /* OPENSSL_VERSION_NUMBER && !OPENSSL_NO_EC && !OPENSSL_IS_AWSLC */
+ 			default:
++#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA))
++				/* OpenSSL does not provide an ID for ML-DSA keys, which is
++				 * apparently a legacy feature */
++				if (EVP_PKEY_is_a(key, "ML-DSA-44") ||
++					EVP_PKEY_is_a(key, "ML-DSA-65") ||
++					EVP_PKEY_is_a(key, "ML-DSA-87"))
++				{
++					return openssl_ml_dsa_private_key_create(key, FALSE);
++				}
++#endif
+ 				EVP_PKEY_free(key);
+ 				break;
+ 		}
+
+diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c
+index 616613a7250..1310dcf87fa 100644
+--- a/src/libstrongswan/utils/leak_detective.c
++++ b/src/libstrongswan/utils/leak_detective.c
+@@ -619,6 +619,7 @@ static char *whitelist[] = {
+ 	"OSSL_PROVIDER_try_load",
+ 	"OSSL_PROVIDER_try_load_ex",
+ 	"OSSL_PROVIDER_load",
++	"OSSL_PROVIDER_add_conf_parameter",
+ 	"RAND_get0_private",
+ 	"RAND_get0_public",
+ 	"RAND_priv_bytes",
+
+diff --git a/src/libstrongswan/tests/suites/test_ml_dsa.c b/src/libstrongswan/tests/suites/test_ml_dsa.c
+index f4920aa2a6e..878bc531149 100644
+--- a/src/libstrongswan/tests/suites/test_ml_dsa.c
++++ b/src/libstrongswan/tests/suites/test_ml_dsa.c
+@@ -26,6 +26,9 @@ struct test_t {
+ 	u_int omega;
+ 	chunk_t asn1_seed;
+ 	chunk_t seed;
++	chunk_t asn1_privkey;
++	chunk_t asn1_seed_key;
++	chunk_t privkey;
+ 	chunk_t asn1_pubkey;
+ 	chunk_t pubkey;
+ 	chunk_t sig;
+@@ -43,6 +46,173 @@ static test_t tests[] = {
+ 	  chunk_from_chars(
+ 		0x93,0xef,0x2e,0x6e,0xf1,0xfb,0x08,0x99,0x9d,0x14,0x2a,0xbe,0x02,0x95,0x48,0x23,
+ 		0x70,0xd3,0xf4,0x3b,0xdb,0x25,0x4a,0x78,0xe2,0xb0,0xd5,0x16,0x8e,0xca,0x06,0x5f),/*   32 */
++	  chunk_from_chars(
++	  	0x30,0x82,0x0a,0x18,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,
++	  	0x03,0x04,0x03,0x11,0x04,0x82,0x0a,0x04,0x04,0x82,0x0a,0x00),
++	  chunk_from_chars(
++	  	0x30,0x82,0x0a,0x3e,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,
++	  	0x03,0x04,0x03,0x11,0x04,0x82,0x0a,0x2a,0x30,0x82,0x0a,0x26,0x04,0x20),
++	  chunk_from_chars(
++		0xbc,0x5f,0xf8,0x10,0xeb,0x08,0x90,0x48,0xb8,0xab,0x30,0x20,0xa7,0xbd,0x3b,0x16,
++		0xc0,0xe0,0xca,0x3d,0x6b,0x97,0xe4,0x64,0x6c,0x2c,0xca,0xe0,0xbb,0xf1,0x9e,0xf7,
++		0xba,0x2b,0x57,0xc4,0x46,0x55,0x6e,0xe2,0xb7,0x2c,0x78,0xb9,0x6b,0xb7,0xa8,0x50,
++		0x3d,0xe4,0x0a,0xfb,0x54,0x18,0x4e,0x3b,0x54,0x63,0xc2,0x1a,0xf7,0x48,0x53,0x23,
++		0xdf,0x98,0xf0,0x16,0x0a,0xe5,0xd1,0x37,0x51,0x27,0x25,0xf8,0x9d,0x56,0x3b,0xc9, /*   80 */
++		0xa1,0x89,0xd3,0x1d,0x20,0xb3,0xb3,0xc8,0xff,0xaa,0xf5,0xe4,0x86,0xe7,0x90,0x51,
++		0xf6,0xf3,0x60,0x5c,0xca,0x25,0x69,0xfd,0xb4,0x6b,0x33,0x18,0xd2,0x38,0x42,0xce,
++		0x40,0xd6,0x43,0x86,0x13,0xf6,0x8b,0x45,0x5b,0x0d,0x3b,0xca,0x0e,0x05,0x0d,0x4d,
++		0x11,0x99,0x88,0xa2,0xc4,0x80,0x1b,0x90,0x84,0xe0,0xb0,0x48,0xc9,0x28,0x09,0x22,
++		0x30,0x90,0x24,0x06,0x49,0x98,0x40,0x65,0x5a,0x26,0x8a,0xda,0x32,0x90,0xda,0x48, /*  160 */
++		0x08,0x22,0x81,0x90,0xc8,0x14,0x61,0xdc,0x16,0x6a,0x21,0x47,0x8e,0x08,0xb2,0x21,
++		0xe3,0x08,0x68,0x1a,0x02,0x44,0x14,0xc6,0x65,0xe1,0x98,0x71,0x90,0xc6,0x69,0x0c,
++		0x15,0x44,0xc9,0xa0,0x11,0xcc,0x34,0x71,0x83,0x40,0x45,0x00,0x12,0x4d,0x91,0x08,
++		0x00,0x83,0x36,0x84,0x12,0x85,0x4c,0xcc,0x00,0x41,0x09,0x90,0x70,0x18,0x95,0x00,
++		0xa2,0xb2,0x85,0x94,0x26,0x41,0x0b,0x00,0x41,0x0a,0xb9,0x80,0xc0,0xc6,0x10,0x0c, /*  240 */
++		0x33,0x29,0xa3,0xa6,0x28,0x1c,0x26,0x10,0x1a,0x37,0x49,0x13,0x35,0x8a,0x0b,0x29,
++		0x2e,0x82,0xa2,0x70,0x8b,0x38,0x49,0x94,0x04,0x80,0x9b,0x26,0x10,0xa4,0x80,0x30,
++		0x04,0x37,0x0c,0x48,0xb2,0x60,0x04,0x17,0x2e,0x19,0x49,0x64,0xc8,0xc4,0x64,0x1a,
++		0x96,0x60,0x10,0x83,0x6d,0x20,0x38,0x22,0x49,0x06,0x08,0x1b,0xb7,0x20,0x01,0x48,
++		0x4d,0xe1,0x10,0x49,0x08,0x38,0x44,0x4c,0x16,0x8e,0x04,0xb2,0x2c,0x09,0x91,0x50, /*  320 */
++		0x83,0x36,0x06,0x5c,0x02,0x8c,0x8a,0x38,0x05,0x1a,0xb5,0x81,0x82,0xc0,0x09,0x51,
++		0x12,0x72,0x22,0x10,0x0d,0x04,0x08,0x2a,0xa4,0x84,0x10,0x58,0x08,0x52,0x08,0x26,
++		0x0a,0x02,0xb6,0x2d,0x8a,0x12,0x0e,0x14,0x22,0x10,0x48,0x44,0x0a,0x14,0x23,0x91,
++		0x50,0x40,0x90,0x0a,0x27,0x92,0xa4,0xb0,0x60,0x08,0x84,0x11,0xc4,0x40,0x22,0x63,
++		0x46,0x2a,0xda,0xa6,0x90,0xc1,0xb2,0x30,0x50,0x10,0x00,0xa0,0xb4,0x6c,0x53,0xb4, /*  400 */
++		0x50,0x13,0x05,0x8d,0x02,0x31,0x0e,0x08,0x20,0x10,0x91,0xc8,0x4c,0x22,0xa7,0x50,
++		0xc2,0xa6,0x70,0x92,0x46,0x41,0x63,0x16,0x8c,0x84,0x00,0x11,0x0b,0x81,0x90,0x13,
++		0x83,0x71,0x21,0x85,0x30,0x1b,0x18,0x46,0xa3,0x10,0x84,0x14,0x43,0x40,0x80,0x98,
++		0x25,0x0c,0x27,0x2c,0x94,0x42,0x41,0xa2,0x88,0x45,0x02,0x13,0x05,0x44,0xb6,0x44,
++		0x52,0x22,0x92,0xd0,0x80,0x50,0x4b,0xa6,0x04,0x84,0x36,0x70,0x09,0xb2,0x4d,0x19, /*  480 */
++		0xa8,0x84,0x24,0x93,0x49,0x94,0xa2,0x80,0x49,0xb4,0x48,0x91,0x28,0x64,0xa1,0xc8,
++		0x65,0x4b,0x82,0x85,0x93,0x30,0x06,0x63,0x12,0x66,0x10,0x37,0x01,0x4a,0x40,0x80,
++		0x18,0x18,0x90,0x44,0xc4,0x0d,0x4b,0x28,0x81,0xa2,0x06,0x40,0xd4,0x30,0x2c,0x1b,
++		0x96,0x4c,0xe1,0xb2,0x60,0x44,0x28,0x41,0xd8,0x30,0x65,0x24,0x09,0x04,0x64,0x00,
++		0x89,0x63,0xc2,0x24,0xc0,0x00,0x49,0x92,0x16,0x52,0x23,0xc1,0x29,0x42,0x26,0x91, /*  560 */
++		0xd0,0x38,0x31,0x83,0x28,0x28,0x4c,0x28,0x61,0x1a,0xb2,0x88,0x80,0x26,0x2d,0x0c,
++		0x19,0x52,0x5b,0x22,0x60,0x8a,0x92,0x28,0xa2,0x18,0x90,0xd9,0x42,0x52,0xcb,0x40,
++		0x8e,0x9b,0x16,0x06,0x4b,0xc8,0x05,0xe0,0x06,0x6c,0x49,0xc2,0x25,0xd4,0x22,0x69,
++		0x14,0x11,0x69,0x1c,0x34,0x90,0x0c,0x85,0x8d,0x1c,0x84,0x49,0x63,0x10,0x85,0x08,
++		0x34,0x89,0x58,0x16,0x66,0xa0,0x38,0x68,0x0b,0xa2,0x50,0xe0,0x84,0x61,0xc1,0x26, /*  640 */
++		0x6e,0xc9,0x14,0x6a,0xc0,0xc0,0x31,0x84,0x18,0x2e,0xdc,0x16,0x52,0x14,0x18,0x91,
++		0x0a,0x39,0x2c,0x83,0xa6,0x8d,0x12,0x29,0x62,0x92,0xa6,0x60,0x08,0xa5,0x10,0xd9,
++		0xc8,0x4d,0x24,0x48,0x24,0x83,0x98,0x24,0x1a,0x44,0x42,0x08,0x08,0x48,0xc8,0x96,
++		0x2d,0xa1,0x10,0x20,0x60,0x24,0x51,0x9c,0x30,0x11,0xdb,0xc8,0x04,0x11,0x22,0x51,
++		0x18,0x04,0x68,0xe2,0x24,0x4e,0x10,0xc4,0x0d,0x18,0x37,0x29,0x11,0xb7,0x84,0x43, /*  720 */
++		0x84,0x8d,0x54,0x40,0x70,0x64,0x80,0x70,0x21,0xb0,0x4d,0x00,0x26,0x62,0x00,0x45,
++		0x25,0x1b,0x83,0x84,0x84,0x80,0x70,0x19,0xb0,0x04,0x0a,0xc9,0x28,0xca,0x80,0x05,
++		0x1b,0x21,0x4e,0xd4,0xb0,0x49,0x8c,0x96,0x6c,0xd0,0x96,0x25,0x48,0x32,0x72,0x54,
++		0x12,0x05,0x09,0x98,0x8c,0x8a,0x32,0x6d,0xc0,0x04,0x31,0x63,0xc8,0x61,0xc8,0xc2,
++		0x68,0x9b,0xb4,0x8c,0x5b,0xc0,0x2d,0xc3,0xa8,0x8d,0x1b,0x16,0x6c,0xd8,0x96,0x41, /*  800 */
++		0x98,0x34,0x02,0x44,0x14,0x2c,0xd3,0x86,0x44,0x0a,0x18,0x8a,0x1a,0x46,0x92,0x1a,
++		0xb7,0x08,0x4c,0x34,0x24,0x21,0x91,0x0d,0x9a,0x96,0x2d,0xcb,0x24,0x22,0x92,0x86,
++		0x08,0x98,0xa0,0x50,0xda,0x44,0x6a,0x19,0xb1,0x05,0x8b,0x20,0x6e,0x24,0xc4,0x81,
++		0xc9,0x98,0x40,0x22,0x19,0x32,0x11,0x05,0x69,0xd3,0x94,0x91,0x08,0xa7,0x71,0xdb,
++		0xc0,0x70,0x02,0xb8,0x28,0x83,0xb4,0x49,0xa0,0x44,0x8a,0x0b,0xb4,0x10,0x61,0x02, /*  880 */
++		0x48,0x1c,0xa6,0x11,0xe3,0x20,0x66,0x40,0xb4,0x70,0x03,0xb6,0x04,0x60,0x48,0x21,
++		0x5b,0x21,0x83,0x39,0x88,0xdb,0x67,0x4a,0x61,0xe7,0xc2,0x08,0xde,0xbe,0x8d,0xae,
++		0x41,0x19,0xaf,0xb0,0x26,0x61,0xa6,0x9a,0xbc,0x8b,0xdd,0x24,0x5b,0x5d,0x0f,0xb1,
++		0xa2,0x67,0x01,0xc9,0xb9,0xc9,0xa8,0xf7,0xd9,0xfc,0xd4,0xc2,0x87,0xff,0x3d,0x60,
++		0x8c,0xf2,0x58,0x28,0x2a,0x1e,0xb2,0x9f,0x93,0x04,0xe8,0x9c,0x14,0xf3,0xe1,0xce, /*  960 */
++		0x56,0x12,0x89,0x1c,0x60,0x29,0x34,0x52,0x60,0x06,0xc9,0x9b,0x4a,0xa2,0x39,0x9b,
++		0xf4,0x94,0xbc,0xf8,0xdf,0x61,0xd6,0xdf,0x4c,0x69,0xbc,0x93,0xe0,0x2d,0x49,0x95,
++		0xe2,0xf7,0x6e,0x9f,0xda,0x4e,0xf6,0x7e,0xb7,0x25,0x6c,0xa8,0x9a,0x3f,0x38,0xfe,
++		0xb2,0xe9,0xdf,0x6a,0x01,0x0d,0xc1,0xc1,0x50,0x02,0xfb,0xd4,0x56,0xfa,0xe8,0x84,
++		0x82,0x1a,0x34,0x16,0x6b,0x06,0x58,0xa2,0x41,0x25,0x95,0x71,0x8e,0x14,0x9b,0xbc, /* 1040 */
++		0x6e,0x22,0x0a,0xee,0x26,0x8d,0x4d,0x82,0x18,0xc2,0x5f,0x6a,0x95,0x7d,0xe5,0xb2,
++		0x6c,0xea,0x7b,0x14,0xcb,0x32,0x0d,0x89,0xe1,0x69,0x9a,0xd9,0xf2,0xb3,0x89,0xc6,
++		0x7e,0xf9,0x33,0x86,0xa2,0xc6,0x5f,0x2c,0x32,0x23,0x33,0x67,0xd7,0x6a,0xe4,0xab,
++		0x2a,0xbb,0xd4,0x22,0xe9,0x8e,0x49,0x3d,0xcc,0x3c,0xc5,0xda,0xf6,0x89,0xb6,0x5c,
++		0xc4,0xbc,0x3f,0xa5,0x1c,0x9c,0x59,0xee,0xaf,0x07,0x55,0x17,0x0c,0x24,0x95,0x80, /* 1120 */
++		0x4d,0x02,0xa6,0x07,0xc5,0xbf,0x88,0x7c,0xd8,0x6a,0x03,0x89,0xf2,0x8f,0xc9,0x72,
++		0x5e,0xf4,0x60,0x03,0xf1,0x3b,0x01,0x87,0x68,0x4b,0xea,0xb1,0xf2,0x4a,0x29,0xf5,
++		0x31,0x96,0x01,0xf3,0x09,0xc9,0x1d,0x2a,0x33,0x3d,0x1b,0x88,0xdf,0x20,0x5a,0x51,
++		0x20,0xc4,0xcf,0xdc,0x22,0x38,0x12,0x4e,0x4e,0x2b,0x47,0xd0,0xb5,0xe6,0x6a,0x65,
++		0x4f,0xe4,0xcc,0xcb,0x07,0x8f,0x07,0xcb,0xd4,0x55,0xd1,0x5d,0x3e,0xec,0x7d,0xa2, /* 1200 */
++		0x74,0xd2,0x4a,0x2e,0x57,0x18,0x84,0xde,0x41,0xc3,0xa9,0xa4,0xfd,0xb3,0xf6,0x09,
++		0x8a,0x17,0x2c,0x30,0x96,0x80,0x39,0xbd,0x0e,0x4e,0xb3,0xe2,0xfb,0x6d,0x6e,0xee,
++		0xd3,0x9e,0x0b,0x63,0x62,0xd5,0x4e,0x7b,0x88,0x95,0x98,0x69,0xdd,0xd5,0xd8,0x73,
++		0xd9,0x65,0x24,0x01,0xa2,0x9f,0x27,0xa2,0x8e,0xa6,0x6d,0x32,0xcc,0xb0,0xef,0x3b,
++		0xf4,0x60,0x0f,0x75,0x57,0xee,0x8d,0x54,0xbf,0x1d,0xad,0x18,0xf4,0x5d,0xdc,0xd4, /* 1280 */
++		0xc9,0xed,0x57,0xb1,0x3e,0x44,0x5b,0xf1,0x22,0xa4,0x3f,0x53,0x94,0x03,0x89,0xbf,
++		0x87,0x14,0xff,0xac,0x72,0x1e,0x59,0x31,0x7e,0x4b,0x70,0x50,0x0a,0xd0,0xd1,0xb9,
++		0xa6,0x27,0x05,0x4d,0x31,0x93,0x20,0x8c,0x77,0x4e,0x0b,0x20,0xed,0x04,0x1a,0x8c,
++		0x05,0x5e,0x75,0xee,0xcd,0x37,0x38,0xf0,0x07,0x15,0x8f,0xad,0xdf,0xca,0x5f,0x43,
++		0x56,0x2d,0x63,0x6a,0x5a,0xcf,0x3a,0x39,0x83,0xd3,0xcf,0xeb,0xca,0x10,0xb8,0x13, /* 1360 */
++		0xf9,0xf6,0x52,0x65,0x19,0x19,0x9a,0x03,0x13,0xcd,0x1d,0xe1,0x3f,0x06,0xad,0x53,
++		0x86,0xe1,0xe1,0x20,0x79,0x5f,0xd2,0x67,0xb7,0xf4,0x20,0x19,0xd8,0x4d,0xf6,0xcd,
++		0x1b,0xf9,0x19,0x30,0xfc,0xa7,0xaf,0xd5,0x2e,0x80,0x70,0x0f,0x4c,0xf5,0xcd,0xc3,
++		0x8a,0x5f,0x7a,0x57,0x49,0x79,0x1c,0x2f,0xdf,0xfc,0x4a,0x10,0x75,0x3c,0x24,0xdb,
++		0x19,0xe8,0xeb,0x65,0x1c,0x5b,0x36,0x32,0x00,0xf0,0xb5,0xd1,0x69,0x94,0x70,0x26, /* 1440 */
++		0xe9,0xf7,0x4f,0x01,0x2d,0xc7,0xc5,0xb3,0x39,0xdd,0x49,0xd2,0x61,0xca,0x1d,0x37,
++		0xf8,0xf2,0x83,0x46,0xe6,0x19,0x78,0x05,0x4f,0x45,0xae,0xe4,0x36,0xdc,0xcb,0xe7,
++		0xbf,0xaf,0xe0,0x7c,0xe9,0xa8,0xb8,0x3c,0x90,0xa2,0x68,0x6f,0xa9,0x54,0x02,0x85,
++		0x09,0x25,0xc8,0x58,0x2b,0xc9,0xb7,0x34,0xe4,0xec,0xa1,0xf7,0xb2,0x0b,0x08,0x6f,
++		0x12,0x9f,0x27,0x7a,0x5c,0xbd,0xaa,0x96,0x3c,0x92,0x71,0x7e,0xf7,0x0e,0xc1,0x9b, /* 1520 */
++		0xf3,0xdb,0xc6,0xda,0x20,0x3a,0xd9,0x0f,0x3b,0x13,0xbb,0xc2,0x2f,0xbd,0x98,0x0b,
++		0xb1,0xb9,0xd3,0xa3,0x44,0x52,0xb3,0x35,0x70,0x21,0xce,0x36,0x13,0x58,0x4e,0x09,
++		0x36,0xbf,0x1d,0x09,0x42,0x09,0x37,0x81,0x5e,0x11,0xcc,0x5d,0x5d,0xdb,0x4b,0xf1,
++		0xd8,0x30,0xc4,0xf8,0x3f,0x30,0xe5,0x15,0x92,0x1c,0x78,0x4d,0x87,0xbb,0x20,0xc0,
++		0x9e,0x3c,0x64,0xbd,0xce,0x9a,0xb1,0xc6,0x9f,0xd3,0x07,0xef,0xe3,0x59,0xc7,0xf9, /* 1600 */
++		0x38,0x56,0x6c,0x9f,0x25,0x17,0xb0,0x63,0x38,0x51,0x67,0xe2,0x47,0xf3,0x10,0x81,
++		0x11,0x9b,0xac,0x6b,0x55,0xa0,0xbd,0xd7,0x14,0x25,0x51,0x0f,0xfa,0x2a,0xbd,0xfa,
++		0x88,0x83,0x76,0xa3,0x7f,0x20,0xc2,0x48,0x01,0x52,0xbb,0x36,0x16,0x34,0x52,0x00,
++		0x07,0xc5,0xb3,0x4b,0xf2,0x28,0x17,0xcb,0x2e,0x67,0xac,0x1a,0x82,0x67,0x0b,0x71,
++		0xf1,0x96,0xc8,0x9f,0x23,0xba,0x31,0x4b,0x16,0xa9,0x48,0x49,0x93,0x04,0xef,0x5c, /* 1680 */
++		0x03,0xdc,0xf5,0x8e,0x52,0xbe,0x31,0x48,0x63,0xe7,0x23,0xc5,0x6d,0x3a,0xeb,0x34,
++		0x0b,0xff,0x18,0xab,0xfa,0x20,0xdc,0x03,0x44,0x20,0x30,0x23,0x05,0x33,0xd9,0x12,
++		0x9b,0x83,0xed,0x22,0xc3,0x51,0xf2,0x32,0x81,0x72,0xe3,0x63,0x44,0x74,0x44,0xae,
++		0x5c,0x69,0x02,0xb7,0x92,0x79,0x9f,0x54,0x44,0x50,0x78,0x71,0x19,0x61,0x2e,0x9b,
++		0xb4,0x13,0x0a,0x33,0xa2,0xa5,0x96,0x2a,0xc0,0x9d,0x57,0x7d,0x6d,0xdc,0x88,0x1f, /* 1760 */
++		0xe6,0x61,0x61,0x26,0xd8,0xa0,0xa7,0xdf,0x2b,0x22,0x53,0xbc,0x8e,0xc4,0xe3,0x53,
++		0x86,0xea,0x55,0x11,0xf0,0xf1,0x58,0x87,0x14,0x5b,0x6c,0x23,0xab,0x3d,0x40,0x33,
++		0x39,0xe4,0x04,0x07,0x3e,0xd9,0xc6,0xa8,0x96,0xa2,0xf9,0xec,0x70,0xc4,0x4b,0xd2,
++		0xae,0xc1,0x0f,0xc4,0x36,0x0e,0x87,0x63,0x6b,0xe1,0x55,0xb6,0xa6,0x7b,0x7e,0xdf,
++		0x38,0xcf,0x73,0x00,0x48,0x13,0xc9,0xe7,0xd2,0xc6,0x54,0xc2,0x53,0x0a,0x71,0xe5, /* 1840 */
++		0xf8,0xc1,0x09,0x42,0xfb,0x6d,0x88,0x41,0x53,0x5a,0xb1,0xda,0x43,0xe8,0xcb,0x0b,
++		0xb8,0x9e,0x78,0xec,0x91,0xf8,0xde,0x15,0x31,0xa0,0x36,0x65,0xcc,0xd5,0xa7,0x5b,
++		0xda,0x0e,0xd0,0xe5,0x98,0x64,0xee,0xef,0x51,0xa8,0x3f,0xa5,0x53,0xaf,0x66,0x2a,
++		0xee,0x00,0xd1,0xf8,0x36,0x7b,0x4d,0x5d,0xdd,0xc3,0x45,0x54,0x4c,0x6b,0xd5,0x14,
++		0xf8,0x88,0xe6,0x03,0x3c,0x25,0x5d,0xb6,0x50,0xda,0x73,0x4a,0xd3,0x3a,0x3c,0xf8, /* 1920 */
++		0x4b,0xd3,0xf0,0x6f,0xa1,0xa7,0xca,0x02,0xe4,0xb8,0xe9,0x93,0xae,0x7a,0xe6,0x34,
++		0x20,0xa4,0x6b,0xa8,0xa3,0x81,0x3d,0x1e,0x9d,0x29,0x66,0xbb,0x85,0x60,0xd7,0x1c,
++		0x62,0xa0,0x44,0xea,0x94,0x17,0x9f,0x4e,0xb1,0xb6,0xed,0x60,0x71,0x9d,0x51,0xe0,
++		0xee,0xf6,0xcd,0x07,0x91,0x52,0xf6,0xbe,0x48,0x8e,0xc9,0x19,0x11,0xc6,0xd3,0xf1,
++		0xd1,0x17,0x3c,0x54,0x1f,0x9d,0x25,0xbf,0x34,0x2f,0xca,0xa3,0xff,0x46,0xc1,0x8f, /* 2000 */
++		0x2a,0x04,0x41,0xd8,0x3b,0xde,0x35,0x46,0xa9,0x82,0x6c,0x34,0x96,0xe0,0x6f,0x2f,
++		0x2b,0x0e,0xeb,0x9d,0x5b,0xe8,0x73,0x9f,0x83,0xa4,0x2d,0x3b,0x30,0x0e,0x70,0xee,
++		0x84,0xdf,0xff,0xb2,0x07,0x64,0xa0,0x60,0x21,0x2f,0x05,0x8c,0x8a,0x5f,0xfa,0x9a,
++		0x34,0xe9,0x28,0xd6,0xa7,0xe0,0x77,0x08,0xfe,0x53,0x93,0xe3,0x01,0x7c,0xe4,0x70,
++		0xeb,0x96,0x58,0xa7,0x4e,0x49,0x51,0xe6,0xfa,0x48,0x54,0xc9,0xe9,0xc2,0x89,0x88, /* 2080 */
++		0x81,0x2e,0x44,0x18,0xa2,0xe8,0x32,0x58,0x0b,0x4a,0x27,0x03,0x72,0xbc,0x69,0x67,
++		0x68,0x89,0xd0,0xcc,0x43,0x24,0x0e,0xda,0xbc,0x1d,0x31,0x14,0xd8,0xf3,0x5a,0xb2,
++		0xe9,0xea,0x95,0x30,0x82,0xe9,0x53,0x62,0x79,0xac,0xb3,0xbe,0x16,0xd3,0xa2,0x05,
++		0xf4,0x6c,0xb6,0x7b,0x22,0x14,0x96,0x93,0x5a,0xc0,0x42,0x92,0xbb,0xfb,0x9a,0x61,
++		0xc0,0xa0,0x3e,0xf4,0xc9,0xb6,0x82,0x04,0x95,0xf3,0xd8,0x0e,0x4a,0x6f,0xb7,0xe1, /* 2160 */
++		0xc6,0x99,0x03,0xfa,0x22,0x6e,0x02,0x3e,0x95,0xba,0x41,0x6d,0xf2,0xe5,0xe4,0x54,
++		0x1e,0x15,0xdc,0xc0,0x00,0xb5,0xe6,0x5c,0x97,0x20,0xda,0xf6,0x96,0x01,0x2f,0xa2,
++		0xa6,0xcf,0x75,0x8e,0xd6,0xd2,0x25,0xa3,0xe4,0xfe,0xe4,0x5a,0xc5,0xfb,0x48,0x70,
++		0x7f,0xae,0x13,0x3d,0x59,0x2c,0xfd,0x2e,0x8c,0x43,0xc2,0x12,0x6f,0x65,0x2b,0xee,
++		0x9b,0xab,0x43,0xa1,0xa1,0x0b,0xe2,0x41,0x1a,0x67,0x94,0xb2,0x6c,0xb5,0x5c,0xc2, /* 2240 */
++		0x17,0xeb,0x7b,0x0b,0x14,0x6d,0x23,0xf7,0x92,0x2d,0x32,0x22,0xae,0x5e,0xe8,0xc6,
++		0xd3,0x8e,0x83,0x99,0xba,0x51,0xc6,0x81,0xb8,0x38,0x16,0xfc,0xf7,0x44,0x38,0x82,
++		0x59,0x20,0xf9,0xce,0x8a,0x20,0x2a,0x8f,0x6d,0x94,0x2d,0xa8,0x62,0x38,0xfb,0x4c,
++		0x9f,0x21,0x98,0xea,0x8d,0xff,0x81,0xc1,0x72,0x86,0xe0,0x18,0xdf,0x4b,0x7f,0xe3,
++		0x88,0x4d,0x17,0x59,0xe4,0xc5,0x9b,0xb5,0x26,0x17,0xae,0xd4,0xe7,0x8e,0x4e,0x7c, /* 2320 */
++		0x4e,0x9a,0x36,0xe4,0xe9,0x96,0xd3,0x23,0x91,0xa3,0x4a,0x0d,0xaa,0xab,0x6b,0x54,
++		0x08,0x15,0xa3,0x4d,0x20,0x40,0x7a,0xef,0x81,0x94,0x9b,0xe6,0x7b,0x90,0x69,0x50,
++		0xd8,0x9b,0xe9,0xf0,0x85,0xe9,0x9e,0xb5,0x87,0x26,0x95,0x17,0x3b,0x3e,0xfa,0xca,
++		0xe9,0x45,0x5d,0x2b,0x2c,0xd4,0xf7,0x10,0xb8,0x72,0xcf,0x66,0x2b,0x73,0x62,0x16,
++		0xb1,0xbb,0xfb,0x1f,0x5f,0x3d,0x48,0x6c,0x7b,0x4b,0x87,0x56,0x12,0x33,0x3f,0x8e, /* 2400 */
++		0x4b,0xa9,0x33,0xdc,0x79,0xf0,0xed,0xfd,0x7b,0xaa,0xde,0x2c,0x16,0xf2,0x14,0x6a,
++		0x49,0x6f,0x79,0xc4,0x2a,0x4d,0x6b,0x52,0x39,0xa3,0x0d,0xd3,0xc4,0x8b,0xeb,0x09,
++		0x2c,0xa0,0x75,0x00,0x10,0xf6,0x9e,0xd4,0xb9,0x23,0x20,0x14,0x7d,0xbb,0xe2,0x08,
++		0xf6,0xe8,0xeb,0x1c,0xf2,0x47,0xd2,0x1a,0x3a,0x3b,0x01,0xdf,0x58,0xc0,0xaa,0x62,
++		0x94,0x4d,0xa0,0xef,0x04,0x50,0xe8,0xce,0x48,0xaa,0x13,0x7e,0x7e,0x15,0x16,0xc1, /* 2480 */
++		0xd5,0xc8,0x6e,0xea,0x17,0xfd,0xfa,0xc1,0x69,0x07,0x46,0xe7,0x26,0x70,0x45,0xa3,
++		0xe9,0x05,0x96,0xbd,0xb7,0x5d,0x50,0xb6,0xdd,0x5c,0x34,0xe5,0xc8,0xd8,0x9d,0xc6,
++		0xf2,0xf1,0xd2,0x44,0x40,0xe5,0x7b,0x47,0x59,0xb8,0x62,0x5f,0x72,0xbc,0x4a,0x7b,
++		0x10,0xd5,0x19,0xd3,0x31,0xf9,0xc4,0x00,0xaa,0xe1,0xe5,0x0d,0x48,0x0c,0xaa,0xe5,
++		0xa1,0xc0,0xfa,0x99,0xd7,0x79,0x24,0xcf,0x8d,0xfe,0x56,0xcd,0x70,0x92,0xe7,0xb9), /* 2560 */
+ 	  chunk_from_chars(
+ 		0x30,0x82,0x05,0x32,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,
+ 		0x11,0x03,0x82,0x05,0x21,0x00),
+@@ -443,6 +613,265 @@ static test_t tests[] = {
+ 	  chunk_from_chars(
+ 		0x70,0xce,0xfb,0x9a,0xed,0x5b,0x68,0xe0,0x18,0xb0,0x79,0xda,0x82,0x84,0xb9,0xd5,
+ 		0xca,0xd5,0x49,0x9e,0xd9,0xc2,0x65,0xff,0x73,0x58,0x80,0x05,0xd8,0x5c,0x22,0x5c),/*   32 */
++	  chunk_from_chars(
++	  	0x30,0x82,0x0f,0xd8,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,
++	  	0x03,0x04,0x03,0x12,0x04,0x82,0x0f,0xc4,0x04,0x82,0x0f,0xc0),
++	  chunk_from_chars(
++	  	0x30,0x82,0x0f,0xfe,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,
++	  	0x03,0x04,0x03,0x12,0x04,0x82,0x0f,0xea,0x30,0x82,0x0f,0xe6,0x04,0x20),
++	  chunk_from_chars(
++		0xd2,0xfd,0x03,0xf3,0xa1,0xb7,0xf6,0x35,0xaf,0x9f,0x34,0xd5,0x80,0xa9,0x8f,0x52,
++		0x4c,0x73,0x5b,0xd5,0xba,0x23,0x55,0xdc,0x6e,0x03,0x5b,0xd2,0x17,0x65,0x58,0x0c,
++		0xe3,0x8d,0x1c,0x14,0xf6,0x46,0x7c,0x35,0xa9,0xf3,0x80,0xd2,0x7d,0xe6,0x1f,0x7c,
++		0x75,0x03,0x15,0x69,0xea,0x2e,0xc8,0x26,0x0e,0xee,0x91,0x05,0x26,0x1b,0x7f,0xe1,
++		0x60,0xc9,0x13,0x44,0xb0,0xc6,0x76,0x4c,0x20,0x4e,0x5b,0x8d,0x42,0x46,0x50,0xbe, /*   80 */
++		0xc0,0x6b,0x9e,0x2e,0x62,0x5a,0xf0,0x7e,0x23,0xf4,0x95,0x0c,0xa2,0x4f,0xb4,0xd6,
++		0xec,0x2c,0x8b,0x3a,0x71,0x7c,0x93,0x11,0xeb,0x87,0x27,0x9f,0xe2,0x5e,0x31,0x1f,
++		0x48,0xb8,0x25,0x65,0x01,0xf6,0x46,0x34,0x12,0xb5,0x0d,0xbc,0x89,0xa8,0x69,0xba,
++		0x22,0x41,0x11,0x26,0x48,0x40,0x07,0x38,0x73,0x02,0x12,0x44,0x25,0x44,0x57,0x54,
++		0x83,0x72,0x50,0x33,0x35,0x62,0x58,0x42,0x32,0x01,0x62,0x11,0x83,0x61,0x02,0x45, /*  160 */
++		0x66,0x56,0x48,0x35,0x61,0x20,0x84,0x52,0x60,0x68,0x50,0x45,0x65,0x55,0x12,0x72,
++		0x47,0x47,0x21,0x21,0x25,0x40,0x22,0x21,0x42,0x81,0x17,0x65,0x03,0x06,0x42,0x61,
++		0x52,0x13,0x43,0x25,0x24,0x33,0x82,0x12,0x11,0x35,0x62,0x33,0x32,0x07,0x47,0x86,
++		0x22,0x31,0x50,0x83,0x70,0x84,0x26,0x43,0x45,0x64,0x51,0x48,0x31,0x14,0x86,0x24,
++		0x66,0x86,0x74,0x33,0x71,0x36,0x67,0x26,0x01,0x47,0x07,0x72,0x11,0x61,0x58,0x85, /*  240 */
++		0x58,0x38,0x71,0x83,0x80,0x67,0x01,0x65,0x78,0x70,0x64,0x77,0x85,0x60,0x02,0x88,
++		0x53,0x48,0x46,0x62,0x25,0x83,0x54,0x88,0x04,0x74,0x40,0x12,0x57,0x43,0x71,0x07,
++		0x75,0x44,0x38,0x71,0x21,0x14,0x22,0x08,0x88,0x72,0x23,0x58,0x87,0x46,0x14,0x85,
++		0x53,0x71,0x67,0x73,0x82,0x28,0x22,0x74,0x14,0x03,0x57,0x73,0x28,0x71,0x83,0x80,
++		0x78,0x14,0x34,0x87,0x52,0x07,0x64,0x74,0x01,0x60,0x75,0x61,0x06,0x08,0x61,0x32, /*  320 */
++		0x21,0x46,0x15,0x65,0x42,0x67,0x08,0x20,0x84,0x10,0x73,0x13,0x03,0x61,0x02,0x86,
++		0x50,0x45,0x26,0x12,0x16,0x68,0x33,0x55,0x25,0x84,0x73,0x53,0x54,0x52,0x65,0x17,
++		0x10,0x60,0x00,0x38,0x57,0x77,0x81,0x24,0x26,0x80,0x41,0x46,0x43,0x26,0x67,0x41,
++		0x06,0x03,0x55,0x41,0x28,0x33,0x37,0x25,0x23,0x06,0x77,0x82,0x15,0x16,0x31,0x73,
++		0x00,0x08,0x75,0x26,0x58,0x46,0x34,0x63,0x88,0x08,0x84,0x64,0x51,0x11,0x24,0x05, /*  400 */
++		0x32,0x10,0x11,0x18,0x18,0x64,0x78,0x22,0x41,0x00,0x38,0x55,0x75,0x42,0x10,0x46,
++		0x83,0x43,0x73,0x38,0x80,0x07,0x83,0x43,0x78,0x74,0x13,0x57,0x62,0x32,0x68,0x80,
++		0x65,0x86,0x48,0x53,0x48,0x35,0x51,0x58,0x50,0x74,0x46,0x05,0x88,0x70,0x07,0x72,
++		0x01,0x31,0x00,0x87,0x54,0x88,0x14,0x20,0x84,0x16,0x61,0x15,0x60,0x56,0x85,0x11,
++		0x58,0x08,0x05,0x88,0x63,0x01,0x82,0x86,0x13,0x14,0x17,0x22,0x01,0x68,0x17,0x17, /*  480 */
++		0x86,0x58,0x53,0x10,0x62,0x28,0x52,0x82,0x26,0x15,0x04,0x31,0x42,0x88,0x54,0x31,
++		0x78,0x05,0x80,0x11,0x50,0x45,0x68,0x82,0x33,0x66,0x36,0x36,0x40,0x65,0x15,0x24,
++		0x47,0x67,0x06,0x45,0x36,0x42,0x26,0x86,0x75,0x06,0x35,0x41,0x33,0x47,0x85,0x12,
++		0x17,0x80,0x83,0x87,0x65,0x51,0x42,0x31,0x38,0x87,0x56,0x62,0x05,0x17,0x40,0x85,
++		0x28,0x14,0x17,0x21,0x38,0x12,0x60,0x81,0x24,0x41,0x45,0x75,0x01,0x82,0x87,0x10, /*  560 */
++		0x10,0x02,0x13,0x25,0x57,0x04,0x21,0x72,0x42,0x78,0x61,0x11,0x70,0x05,0x30,0x47,
++		0x72,0x13,0x20,0x30,0x21,0x67,0x44,0x31,0x57,0x71,0x45,0x57,0x10,0x54,0x16,0x65,
++		0x74,0x15,0x24,0x02,0x43,0x71,0x51,0x20,0x55,0x11,0x67,0x83,0x67,0x82,0x52,0x53,
++		0x35,0x66,0x42,0x46,0x13,0x70,0x22,0x32,0x74,0x00,0x07,0x06,0x81,0x87,0x17,0x57,
++		0x80,0x28,0x68,0x01,0x72,0x10,0x04,0x27,0x55,0x22,0x86,0x42,0x53,0x15,0x81,0x76, /*  640 */
++		0x30,0x86,0x40,0x83,0x11,0x43,0x30,0x53,0x82,0x73,0x53,0x03,0x72,0x35,0x68,0x70,
++		0x45,0x41,0x15,0x73,0x14,0x12,0x31,0x64,0x32,0x66,0x63,0x56,0x21,0x51,0x50,0x82,
++		0x10,0x30,0x23,0x38,0x17,0x21,0x27,0x10,0x23,0x14,0x22,0x75,0x77,0x28,0x37,0x71,
++		0x62,0x75,0x06,0x88,0x72,0x14,0x18,0x73,0x13,0x03,0x01,0x50,0x71,0x58,0x62,0x86,
++		0x62,0x88,0x86,0x86,0x03,0x27,0x01,0x46,0x17,0x22,0x71,0x38,0x53,0x81,0x70,0x33, /*  720 */
++		0x88,0x68,0x13,0x78,0x81,0x04,0x86,0x57,0x30,0x16,0x52,0x31,0x40,0x83,0x07,0x56,
++		0x82,0x10,0x32,0x31,0x28,0x50,0x06,0x50,0x81,0x63,0x06,0x75,0x76,0x65,0x11,0x60,
++		0x14,0x17,0x12,0x12,0x55,0x56,0x48,0x11,0x41,0x13,0x28,0x82,0x62,0x07,0x47,0x64,
++		0x24,0x48,0x23,0x24,0x77,0x53,0x26,0x08,0x17,0x58,0x11,0x56,0x37,0x48,0x35,0x51,
++		0x47,0x86,0x85,0x66,0x66,0x81,0x73,0x20,0x21,0x36,0x75,0x22,0x74,0x66,0x83,0x44, /*  800 */
++		0x57,0x00,0x66,0x64,0x77,0x20,0x47,0x22,0x28,0x56,0x87,0x12,0x47,0x02,0x48,0x07,
++		0x02,0x54,0x23,0x01,0x25,0x71,0x37,0x36,0x75,0x36,0x00,0x52,0x68,0x15,0x33,0x35,
++		0x82,0x06,0x13,0x73,0x24,0x08,0x71,0x76,0x15,0x22,0x42,0x60,0x18,0x53,0x43,0x11,
++		0x64,0x57,0x76,0x17,0x61,0x56,0x68,0x76,0x60,0x65,0x54,0x78,0x10,0x33,0x63,0x14,
++		0x21,0x83,0x21,0x60,0x15,0x55,0x80,0x42,0x38,0x42,0x03,0x13,0x12,0x34,0x36,0x25, /*  880 */
++		0x27,0x30,0x82,0x81,0x25,0x47,0x51,0x35,0x44,0x12,0x67,0x35,0x00,0x10,0x01,0x83,
++		0x85,0x74,0x42,0x40,0x13,0x03,0x61,0x27,0x81,0x26,0x26,0x81,0x18,0x87,0x43,0x51,
++		0x20,0x62,0x71,0x27,0x51,0x56,0x10,0x22,0x22,0x81,0x11,0x81,0x41,0x66,0x66,0x38,
++		0x20,0x86,0x75,0x56,0x12,0x40,0x06,0x54,0x61,0x12,0x74,0x40,0x34,0x58,0x58,0x78,
++		0x10,0x07,0x85,0x25,0x72,0x88,0x57,0x22,0x22,0x25,0x50,0x84,0x00,0x41,0x26,0x08, /*  960 */
++		0x36,0x46,0x28,0x78,0x46,0x78,0x05,0x02,0x28,0x20,0x77,0x13,0x60,0x75,0x14,0x43,
++		0x68,0x78,0x64,0x31,0x38,0x77,0x73,0x73,0x55,0x41,0x27,0x00,0x54,0x07,0x08,0x28,
++		0x68,0x80,0x04,0x53,0x83,0x43,0x22,0x81,0x00,0x64,0x35,0x48,0x67,0x66,0x50,0x17,
++		0x75,0x76,0x12,0x75,0x43,0x81,0x62,0x40,0x33,0x43,0x45,0x38,0x87,0x21,0x66,0x14,
++		0x70,0x48,0x41,0x43,0x14,0x66,0x58,0x78,0x45,0x82,0x02,0x25,0x45,0x73,0x15,0x21, /* 1040 */
++		0x32,0x03,0x02,0x48,0x80,0x80,0x13,0x71,0x25,0x54,0x32,0x72,0x05,0x68,0x65,0x24,
++		0x68,0x04,0x06,0x16,0x83,0x50,0x54,0x53,0x37,0x37,0x27,0x22,0x20,0x68,0x08,0x25,
++		0x50,0x84,0x72,0x86,0x74,0x22,0x36,0x16,0x80,0x07,0x55,0x18,0x12,0x17,0x84,0x44,
++		0x81,0x15,0x64,0x50,0x71,0x10,0x58,0x15,0x51,0x10,0x10,0x47,0x16,0x21,0x07,0x58,
++		0x61,0x18,0x78,0x00,0x52,0x72,0x64,0x52,0x17,0x43,0x23,0x40,0x76,0x48,0x67,0x30, /* 1120 */
++		0x77,0x63,0x64,0x87,0x51,0x31,0x63,0x84,0x68,0x74,0x53,0x63,0x84,0x23,0x54,0x66,
++		0x10,0x48,0x36,0x33,0x85,0x21,0x48,0x42,0x03,0x82,0x51,0x10,0x33,0x57,0x46,0x80,
++		0x16,0x43,0x34,0x02,0x07,0x03,0x53,0x22,0x12,0x75,0x73,0x34,0x65,0x83,0x33,0x87,
++		0x43,0x85,0x17,0x50,0x36,0x60,0x88,0x02,0x58,0x75,0x80,0x88,0x31,0x63,0x60,0x18,
++		0x21,0x32,0x26,0x15,0x68,0x74,0x11,0x10,0x33,0x14,0x13,0x05,0x34,0x16,0x72,0x65, /* 1200 */
++		0x35,0x50,0x13,0x34,0x80,0x87,0x10,0x26,0x48,0x68,0x84,0x52,0x71,0x44,0x23,0x58,
++		0x80,0x35,0x57,0x70,0x54,0x84,0x28,0x70,0x55,0x88,0x86,0x83,0x86,0x25,0x21,0x82,
++		0x72,0x61,0x17,0x78,0x85,0x17,0x67,0x73,0x00,0x57,0x71,0x11,0x78,0x51,0x10,0x65,
++		0x63,0x57,0x02,0x87,0x40,0x13,0x40,0x01,0x26,0x53,0x45,0x12,0x05,0x46,0x75,0x18,
++		0x80,0x70,0x33,0x35,0x66,0x22,0x62,0x00,0x70,0x23,0x26,0x87,0x72,0x63,0x11,0x13, /* 1280 */
++		0x33,0x33,0x81,0x41,0x70,0x62,0x28,0x61,0x51,0x47,0x31,0x30,0x25,0x46,0x51,0x17,
++		0x61,0x58,0x07,0x41,0x61,0x37,0x37,0x06,0x14,0x00,0x54,0x88,0x77,0x75,0x67,0x77,
++		0x66,0x53,0x16,0x72,0x66,0x66,0x88,0x76,0x43,0x58,0x31,0x04,0x87,0x57,0x06,0x76,
++		0x47,0x00,0x43,0x63,0x58,0x60,0x52,0x03,0x44,0x27,0x36,0x48,0x61,0x23,0x72,0x16,
++		0x10,0x62,0x42,0x08,0x60,0x83,0x23,0x54,0x03,0x55,0x55,0x73,0x00,0x61,0x03,0x65, /* 1360 */
++		0x34,0x27,0x14,0x15,0x86,0x62,0x55,0x80,0x16,0x53,0x10,0x18,0x26,0x11,0x35,0x46,
++		0x82,0x46,0x13,0x25,0x83,0x47,0x70,0x50,0x06,0x01,0x56,0x02,0x11,0x68,0x54,0x53,
++		0x03,0x68,0x73,0x36,0x41,0x88,0x86,0x33,0x42,0x52,0x01,0x58,0x33,0x42,0x32,0x88,
++		0x56,0x81,0x77,0x55,0x51,0x48,0x48,0x12,0x01,0x58,0x13,0x85,0x04,0x14,0x71,0x83,
++		0x57,0x07,0x54,0x55,0x54,0x55,0x28,0x27,0x31,0x36,0x02,0x12,0x32,0x68,0x32,0x13, /* 1440 */
++		0x82,0x58,0x70,0x28,0x58,0x53,0x44,0x86,0x72,0x73,0x42,0x84,0x18,0x22,0x08,0x83,
++		0x61,0x02,0x14,0x16,0x17,0x12,0x41,0x57,0x48,0x85,0x25,0x10,0x26,0x07,0x36,0x76,
++		0x12,0x66,0x17,0x21,0x32,0x36,0x03,0x25,0x41,0x10,0x11,0x22,0x66,0x60,0x16,0x16,
++		0x32,0x64,0x26,0x05,0x18,0x63,0x51,0x58,0x51,0x31,0x42,0x53,0x84,0x56,0x66,0x27,
++		0x83,0x33,0x54,0x50,0x76,0x46,0x50,0x80,0x25,0x43,0x41,0x57,0x35,0x78,0x25,0x43, /* 1520 */
++		0x02,0x82,0x38,0x47,0x45,0x70,0x15,0x67,0x51,0x77,0x47,0x80,0x31,0x52,0x75,0x00,
++		0x00,0x94,0x7b,0xca,0x93,0xc2,0x7d,0x58,0x4e,0x2c,0x66,0xea,0xc9,0xc7,0x64,0x0c,
++		0x1c,0xa2,0x17,0xee,0xf6,0x6d,0xab,0xbc,0xb2,0x60,0xb4,0xc3,0x43,0x00,0xfa,0x05,
++		0x13,0x57,0x82,0x0f,0x57,0x39,0x25,0x44,0x98,0x2f,0xd1,0x10,0x57,0xde,0x23,0x3e,
++		0x6d,0x2d,0xd8,0x49,0x72,0xa7,0xe4,0x7d,0x4d,0xba,0x99,0xbc,0x30,0xcf,0x8f,0x2a, /* 1600 */
++		0xd5,0xa2,0xc0,0x24,0x31,0x95,0xed,0x27,0x30,0xff,0xa9,0x2d,0x22,0x7d,0x15,0x30,
++		0x95,0x97,0x2d,0x4b,0x34,0x47,0xff,0xac,0x45,0xa2,0x3e,0xb4,0x1c,0xbc,0x87,0xcd,
++		0xd1,0x25,0x0a,0x8a,0x47,0x8b,0x0f,0x7a,0x1d,0x5b,0x39,0xaa,0x22,0x06,0xe4,0x86,
++		0x45,0x58,0x4f,0xe7,0xbf,0x7a,0x13,0x16,0x8f,0x48,0x27,0x65,0xe5,0x7b,0xb9,0x24,
++		0xac,0x6d,0x9a,0x11,0x36,0x9f,0x4a,0x6a,0xff,0xcd,0x16,0x9b,0x7d,0x75,0x12,0x9b, /* 1680 */
++		0x35,0xd5,0x13,0x4a,0x31,0x76,0x1b,0xb8,0x35,0x5a,0xee,0xed,0x27,0xe2,0x01,0xa0,
++		0x63,0x13,0x01,0x3e,0x30,0x7a,0x01,0xa7,0x3a,0xea,0x79,0x55,0xc0,0x57,0x8c,0x8c,
++		0x5e,0x5a,0x1a,0x2d,0x2f,0xa4,0x59,0x3f,0xac,0xd9,0x04,0xc6,0x20,0x40,0xbd,0xb9,
++		0xf3,0x29,0x93,0x35,0x36,0xbf,0x8d,0x81,0xc4,0x25,0x6b,0xaa,0xe8,0x72,0x3f,0xd4,
++		0xdc,0x66,0xbb,0x5e,0x7f,0x9c,0xa4,0x90,0x31,0xa1,0x93,0xec,0xec,0xbb,0x5d,0xc3, /* 1760 */
++		0x90,0xec,0x6d,0x55,0x13,0xc7,0x9a,0x05,0x2b,0x3f,0xd4,0x36,0x12,0xfb,0x73,0x75,
++		0x31,0x5d,0x80,0x91,0xf7,0x9b,0xab,0x13,0x18,0xf1,0x78,0x54,0x56,0x1b,0xc9,0x3a,
++		0xe0,0xe5,0xcd,0x6d,0x13,0x1e,0x56,0x2c,0x81,0x14,0x81,0x0c,0x93,0x9a,0xe5,0x63,
++		0xaa,0x10,0xb4,0x7c,0xe4,0x48,0x43,0x17,0xf3,0x4a,0xbd,0x02,0xd0,0xcc,0xad,0x58,
++		0xdd,0x29,0xbc,0xf6,0x57,0xbb,0xd9,0x25,0x4b,0x01,0xca,0x97,0x26,0x09,0x19,0x38, /* 1840 */
++		0xed,0x32,0x05,0x4b,0x37,0xdd,0x61,0x72,0x40,0xf4,0x43,0x4c,0x1a,0x4a,0x87,0x11,
++		0xaa,0x3a,0x39,0x9a,0x8a,0x53,0x88,0x33,0x0b,0x70,0x59,0xec,0xcb,0xb6,0xb1,0xb9,
++		0xcf,0x71,0x87,0xad,0xf1,0x0b,0x0c,0x91,0x71,0xd3,0xc0,0xf6,0xe2,0xd4,0x60,0xa4,
++		0x19,0x24,0x76,0x72,0xe3,0xb9,0xfe,0xa2,0xc9,0x59,0x10,0xbf,0x2f,0xb6,0xa5,0xd6,
++		0x1f,0x25,0x74,0x53,0xb0,0x7a,0xfb,0x64,0xb0,0xba,0x27,0x58,0xbc,0xd7,0x35,0x75, /* 1920 */
++		0x1f,0x2d,0x53,0x51,0x5e,0x23,0x6f,0xe8,0xa5,0xb4,0x39,0x3b,0x80,0xbf,0x06,0xdf,
++		0x97,0xbd,0xc6,0x38,0x00,0x87,0xe6,0xaa,0x8d,0xde,0x6e,0x09,0x81,0x11,0xa7,0x34,
++		0x3f,0xcd,0xd1,0xe9,0x03,0x70,0x8e,0x63,0x7e,0xbf,0x28,0x32,0x3c,0xda,0x6b,0x94,
++		0x05,0x81,0x0e,0xdc,0xfb,0x36,0x91,0x14,0x9e,0xcf,0x22,0x4c,0x50,0xf8,0xdf,0x92,
++		0xa9,0x4a,0xa4,0x77,0x0a,0x0e,0x91,0x46,0x61,0x94,0xbb,0x0e,0x27,0xbf,0x1c,0xab, /* 2000 */
++		0xf1,0x6a,0xdf,0xd3,0x51,0x22,0x00,0x33,0xf7,0x6f,0x59,0x25,0x55,0x7b,0xcf,0x96,
++		0x34,0xe9,0x46,0x13,0x59,0x62,0x1d,0x80,0xb4,0xbb,0xad,0x7e,0x2a,0x6e,0x43,0x2d,
++		0xc4,0x3b,0x12,0x6c,0xa4,0x2a,0xb8,0x8a,0xa8,0x8f,0x0a,0x84,0xaf,0x58,0x02,0x9c,
++		0x99,0xa0,0x24,0x8f,0x0c,0x45,0x40,0x71,0xf3,0x5b,0x83,0x1f,0xed,0x12,0x54,0xd6,
++		0xf4,0xe2,0x72,0x04,0x85,0x78,0x62,0x15,0xf7,0xc7,0xf0,0xc4,0xed,0x15,0xfa,0x85, /* 2080 */
++		0x3c,0xd3,0xaa,0x07,0x25,0x9b,0x39,0x24,0x0a,0x82,0x13,0x5c,0x29,0x23,0xa7,0x2b,
++		0x87,0x6f,0xab,0xb3,0xf0,0xf2,0xc0,0x96,0x13,0xde,0x39,0xd4,0x59,0xa0,0x7c,0x14,
++		0xe7,0xba,0x43,0x7d,0x80,0x41,0x49,0x1f,0xce,0xc1,0x43,0x34,0x04,0xba,0xd1,0xda,
++		0x9e,0xe9,0x47,0x1e,0x17,0xcb,0x69,0x1b,0x2a,0x35,0x37,0x10,0xc9,0xff,0xa4,0xe5,
++		0x17,0x81,0x12,0x02,0x77,0x64,0xeb,0x7d,0xe8,0x09,0xc3,0xe1,0xf1,0xfa,0x41,0x78, /* 2160 */
++		0xa5,0xd4,0xdc,0x9e,0xe2,0x78,0x57,0xef,0xf2,0x6b,0x91,0x71,0x1f,0xc1,0x44,0xd5,
++		0xa7,0x75,0xb8,0xb5,0x0d,0x5d,0xb9,0x39,0xba,0x32,0x07,0x68,0x0c,0x24,0x2f,0xc8,
++		0x21,0x94,0x7f,0x93,0x4c,0x8d,0xae,0xe2,0x03,0x56,0x3d,0x28,0x60,0x6b,0xe6,0x24,
++		0xa3,0x29,0x01,0x93,0x2d,0xae,0x85,0x71,0x2a,0xf6,0xc8,0x01,0x60,0x26,0x92,0x7e,
++		0x9b,0x81,0x29,0x57,0x4b,0xe3,0xcb,0x1e,0x95,0x33,0x2b,0x05,0x27,0x07,0xac,0x8a, /* 2240 */
++		0xa8,0xf4,0x35,0xe8,0x8b,0x7e,0x56,0x8d,0x49,0x87,0xc6,0xac,0x0e,0x90,0x2b,0x06,
++		0x09,0xa0,0x2d,0x91,0xb3,0xf5,0xfd,0x3f,0xd9,0x01,0xdd,0xd0,0xdb,0x98,0x73,0xbd,
++		0x7c,0x71,0xed,0x92,0x1d,0x45,0x77,0xa7,0x8c,0x4f,0xcc,0x9b,0xf0,0x75,0x20,0x3d,
++		0x38,0xf5,0xe7,0x6e,0x74,0xf2,0x77,0x48,0x4e,0x05,0x7b,0x61,0x89,0x00,0x41,0x31,
++		0xb0,0xc9,0xb1,0xa1,0x55,0x29,0x4d,0x1c,0xd3,0xd5,0x20,0x8e,0x26,0x69,0x01,0xd7, /* 2320 */
++		0xd3,0x14,0xfa,0xcc,0xe7,0xe2,0xaa,0x58,0x45,0x83,0xa1,0x1e,0x4d,0x7c,0x21,0xb9,
++		0x4a,0x32,0xe5,0x08,0xed,0xdb,0xbd,0x7a,0x65,0xaa,0x86,0xb4,0xfd,0xfa,0x6b,0xc2,
++		0x85,0xd4,0xcf,0xf5,0x39,0x26,0xc7,0x17,0x3f,0xbe,0x1f,0x89,0xcc,0x30,0x32,0x34,
++		0xb8,0x78,0xc6,0xb8,0x10,0x1f,0x58,0xac,0x8d,0x3e,0x5e,0x1b,0xf5,0xab,0x6b,0x26,
++		0x29,0x7c,0xc9,0x7b,0x95,0x95,0x4a,0xab,0xdb,0x25,0xbe,0x00,0x8a,0x3f,0x47,0xe5, /* 2400 */
++		0x64,0x87,0xb0,0x0d,0x3d,0xed,0xa8,0x90,0xd9,0x2c,0x83,0x95,0x7f,0xea,0xc6,0xb8,
++		0x29,0x1a,0xf6,0x59,0x59,0xe1,0xd1,0xfc,0xa3,0xbd,0x19,0x6e,0x9f,0xc9,0xe6,0x7e,
++		0x06,0x07,0x09,0x48,0x22,0xe5,0xb4,0x19,0x1d,0xb9,0x68,0x24,0xb9,0xf0,0x3f,0x2e,
++		0xf5,0x7f,0x52,0x38,0xba,0x7e,0x1e,0x84,0xed,0x55,0xb7,0xdf,0xf3,0xd6,0xc2,0xc1,
++		0x27,0x36,0x92,0xa9,0xa1,0x92,0x72,0x16,0x61,0x30,0xdb,0x89,0xfc,0x67,0xdc,0x94, /* 2480 */
++		0xdb,0x61,0x4e,0x3e,0x82,0xba,0x3a,0x35,0x12,0xb0,0x12,0xd5,0x1f,0xb4,0x86,0xb5,
++		0xa3,0x15,0x0b,0x78,0xe7,0x24,0xe2,0xa1,0x2d,0xe0,0x7d,0x86,0x71,0xfb,0xa2,0xda,
++		0x7f,0xd5,0xd1,0x47,0x20,0x8f,0xc3,0xaf,0x65,0x3e,0x65,0x20,0xfc,0x40,0x87,0x1a,
++		0xf2,0x17,0x7e,0x65,0xcb,0xd0,0xea,0xf3,0x04,0x21,0x7b,0x36,0x7a,0x66,0x5f,0x22,
++		0x4c,0xae,0xdf,0xe9,0x30,0x06,0xac,0x1e,0x14,0xbc,0xd6,0x7a,0x88,0xd1,0x71,0xf3, /* 2560 */
++		0xd8,0xf3,0xe3,0x58,0xa7,0x19,0x26,0xba,0x3e,0x5c,0x23,0x9a,0x53,0x12,0x63,0xec,
++		0x94,0x37,0xbf,0x2a,0x03,0x3b,0x8b,0x55,0xb2,0xc0,0xcb,0x6e,0x7e,0x97,0x31,0x6e,
++		0x22,0xdf,0x77,0xca,0xd9,0x10,0xd2,0x0e,0xec,0xe1,0xc5,0x09,0x10,0xa5,0xcc,0x32,
++		0xad,0xab,0x09,0x37,0x75,0x50,0xf9,0x2d,0x5b,0xb1,0xf4,0xc0,0x7f,0x4a,0x28,0x22,
++		0x33,0x8e,0x2c,0xff,0x53,0x48,0xdf,0x77,0xcf,0x8e,0xf8,0xe6,0x65,0x7d,0xed,0x1e, /* 2640 */
++		0x0c,0xe0,0x58,0xe3,0xcc,0xfb,0xf3,0x9b,0x3f,0x16,0x6e,0x30,0x3d,0x33,0xc3,0x55,
++		0x6c,0x9a,0xc8,0xec,0xb3,0xdf,0x7c,0x74,0xab,0x36,0xd0,0xf2,0x79,0x44,0x41,0xba,
++		0x98,0x08,0x82,0x7b,0x57,0x8f,0xb5,0xc2,0x9e,0x49,0x4e,0x21,0x53,0x9a,0xd3,0xab,
++		0x2b,0x41,0xbf,0x16,0x1d,0x7f,0x69,0x58,0x9d,0x45,0x24,0xc5,0x4c,0x89,0xb4,0x86,
++		0xf7,0x5d,0x25,0x2f,0x54,0x1c,0xc6,0x3b,0x9e,0x70,0x6d,0x64,0xa1,0x28,0x9a,0x23, /* 2720 */
++		0x06,0xc5,0x95,0x36,0x3c,0xb6,0xfb,0xef,0x0a,0x1b,0x5b,0x17,0xab,0x5b,0x17,0x94,
++		0xbf,0x27,0x03,0x6f,0x64,0xea,0xf0,0xbd,0x43,0x0d,0xd5,0x8d,0x80,0x01,0x0c,0xcd,
++		0xad,0xa4,0xa5,0xa3,0xa1,0xe4,0x1a,0x6f,0xbf,0x12,0x9d,0x73,0x77,0x9a,0x37,0xae,
++		0x5c,0x8d,0x68,0x41,0xa9,0x99,0x3c,0x51,0xe3,0x64,0xe0,0x4f,0xac,0x8e,0x25,0xa4,
++		0xe6,0x87,0x2f,0x6c,0x86,0x0f,0xa2,0x65,0xc1,0xc4,0x42,0x6a,0xd9,0xc2,0x1d,0x26, /* 2800 */
++		0xda,0x8c,0x27,0x85,0x46,0xad,0xcd,0x83,0x1f,0x2b,0x8b,0x26,0xd4,0xe1,0xf6,0x70,
++		0x62,0x3d,0x95,0xc8,0x36,0x2d,0xa6,0x62,0xd1,0xff,0x0a,0xb6,0x87,0x50,0x3f,0x32,
++		0x8d,0xe0,0x95,0x81,0x0e,0xde,0x12,0xb4,0x9e,0xad,0x15,0x33,0x51,0x95,0x58,0xc1,
++		0xe9,0x40,0xb4,0x6e,0x4e,0xdb,0x02,0x7b,0xe9,0xda,0x20,0x39,0xb2,0x5d,0xcf,0x73,
++		0x57,0xe1,0x9e,0x54,0x16,0xae,0x26,0x8c,0x14,0xfb,0x3a,0x8b,0xab,0xcb,0x3d,0x23, /* 2880 */
++		0xf7,0x0c,0xc9,0xd5,0x96,0x81,0xc5,0xd8,0x33,0xac,0x22,0xe6,0x53,0xd8,0x6e,0x22,
++		0xce,0x82,0x25,0x40,0x75,0x5d,0x8d,0x24,0x3c,0x15,0x21,0x3d,0x07,0x6c,0x6b,0x26,
++		0x43,0x6d,0xdc,0x07,0xc7,0xe0,0x01,0x34,0x7b,0x0c,0xb8,0x78,0x3d,0xfe,0xfe,0xdf,
++		0x27,0x5f,0xec,0x47,0x92,0x68,0x67,0x34,0x00,0x7f,0x0f,0xf8,0x54,0x08,0x11,0xc2,
++		0xaf,0xe6,0xca,0x15,0x14,0x20,0x53,0x2f,0xa5,0x52,0x6a,0x10,0x74,0xc3,0xd7,0x89, /* 2960 */
++		0xf2,0x93,0x2d,0xe4,0x2e,0x3a,0xcf,0xbf,0x94,0x76,0x0f,0x42,0x6d,0x96,0xcf,0x03,
++		0x3f,0xa4,0x9e,0x2f,0x45,0x8f,0x9a,0x9c,0x2e,0x71,0xda,0xcf,0xe0,0x09,0xdd,0x9c,
++		0x3f,0x3c,0x8a,0xb3,0x28,0x2d,0x6f,0x38,0x3b,0x98,0x1c,0x82,0xd6,0x36,0x4f,0x0e,
++		0x4b,0xdb,0x2a,0xf6,0xa9,0x5b,0xa6,0x1f,0x47,0x41,0x50,0xca,0xd7,0x23,0x3f,0x89,
++		0x03,0xdf,0x97,0x2d,0xbb,0x03,0x28,0xc0,0xcb,0x9d,0x0c,0xcb,0xef,0x88,0x3d,0x2e, /* 3040 */
++		0x6a,0xdd,0x18,0x0e,0xca,0x1b,0x66,0x2f,0xc1,0xd2,0xdb,0xbd,0xdb,0x36,0x34,0x21,
++		0x9e,0x1e,0xff,0x38,0xb1,0xe5,0x28,0x75,0x35,0x6c,0x03,0xea,0xde,0x94,0x20,0x55,
++		0xf4,0x83,0x50,0x4b,0xbb,0xcb,0x43,0x02,0xa4,0x17,0xcf,0x6d,0x32,0x8e,0xd7,0x93,
++		0xb1,0xa3,0xc0,0x96,0x9b,0x7b,0x34,0x18,0xf5,0x0a,0xb3,0x9f,0x83,0xc5,0x66,0x6c,
++		0x90,0xe3,0x83,0x56,0xf7,0xf9,0xd4,0x94,0xa6,0xdc,0xb6,0x3d,0x67,0xc3,0x4e,0x3d, /* 3120 */
++		0x14,0xa4,0xe1,0x55,0x96,0x49,0x79,0x26,0xc8,0x56,0x8d,0x8e,0xc3,0xdb,0xd9,0xc2,
++		0xe8,0x2c,0x38,0x5b,0xcf,0xb8,0xd9,0x67,0x48,0x63,0xbd,0x4f,0xbf,0x17,0x57,0xdb,
++		0x44,0x7b,0xf8,0x04,0xae,0x95,0x01,0x47,0xc9,0x1f,0xbf,0x9a,0xa1,0x78,0x91,0x04,
++		0x4c,0xca,0xa7,0x3b,0x45,0x52,0x85,0x97,0x46,0x2c,0xed,0x75,0x1d,0x01,0x5e,0xbb,
++		0xa9,0xe2,0xb7,0xcd,0xcb,0xe6,0xdc,0x05,0xaa,0x9e,0xae,0x0c,0x86,0x84,0x8a,0x34, /* 3200 */
++		0x75,0xbb,0x1c,0x57,0x44,0xf5,0x90,0x3e,0xe4,0xa8,0x42,0xa4,0x69,0xcc,0x18,0x12,
++		0x71,0xf2,0x45,0xad,0x70,0xd0,0x2a,0x48,0x37,0x86,0x3b,0x29,0x6b,0x4a,0xdb,0x4e,
++		0x8d,0x03,0xd8,0x2b,0x64,0xaa,0x11,0xdd,0x31,0xcd,0xf2,0x1e,0xdf,0x1d,0xfe,0x32,
++		0x76,0xc4,0xdb,0xc8,0x77,0xe3,0x5b,0x15,0xfb,0x28,0x35,0xec,0x3a,0x1c,0x45,0x31,
++		0x68,0xa3,0x8c,0xa8,0xe5,0x63,0xcf,0x3e,0x9a,0x00,0x73,0x6c,0xd5,0xcf,0xbd,0x28, /* 3280 */
++		0x41,0xd1,0x0f,0x94,0xad,0x55,0x79,0x9c,0x29,0x27,0xe5,0x46,0x1b,0x28,0xba,0xc5,
++		0x17,0x4d,0x0c,0xe3,0xf8,0xf7,0xcd,0x76,0x09,0xfb,0xc8,0xda,0x0c,0x38,0xcc,0x21,
++		0x69,0x5c,0xed,0xad,0x12,0xf8,0xd2,0xe6,0x49,0x51,0xa8,0x99,0x6e,0x51,0x0d,0x6d,
++		0x52,0x79,0x7c,0x5b,0xa0,0xeb,0x4a,0xfa,0x6b,0xf2,0xcc,0x43,0xda,0x09,0xde,0x31,
++		0x79,0xe8,0x99,0xbd,0x71,0x88,0xb3,0x2a,0x98,0xa4,0x99,0xd3,0x72,0xf3,0x70,0x7c, /* 3360 */
++		0xed,0x47,0x9b,0x09,0x81,0xcb,0x50,0xc0,0xc0,0x53,0x9c,0xf7,0xe3,0x10,0x0b,0x72,
++		0x0e,0x46,0x66,0x52,0xa4,0xf4,0x99,0xc2,0xba,0x3a,0x17,0xf5,0x23,0x22,0x68,0x73,
++		0x0b,0x96,0x2b,0xc5,0x72,0xc0,0xde,0x96,0xe8,0xc9,0xe2,0x8f,0x7e,0x35,0x32,0xc2,
++		0x22,0x41,0x96,0xaa,0x9e,0x27,0x68,0x8d,0xd0,0x50,0xd7,0xcb,0x78,0x54,0xfb,0x3c,
++		0x35,0xf9,0xc6,0x2e,0xfb,0x10,0xda,0x84,0x83,0x3f,0x29,0xbb,0x1b,0xe5,0xef,0x3b, /* 3440 */
++		0x53,0x36,0x38,0xee,0xf7,0x43,0xd8,0x11,0x9d,0xdc,0x29,0x0b,0xdf,0x08,0xb6,0xf0,
++		0xf9,0xe4,0xe1,0xe1,0x34,0x46,0xc5,0x3e,0xd6,0x98,0x05,0xda,0x26,0x90,0x8a,0x15,
++		0xdf,0x1c,0x48,0xe0,0x09,0xec,0x12,0x53,0xbd,0x5a,0x58,0x98,0xeb,0xb5,0x12,0x1c,
++		0xc2,0x49,0x04,0xc8,0xb1,0x0e,0x24,0xe6,0x80,0xe5,0x65,0x98,0x50,0x76,0xfd,0xa1,
++		0x1d,0x13,0xff,0xdf,0xa4,0xdb,0x28,0xac,0x9f,0x0a,0xea,0x2f,0x81,0xfd,0x7e,0xd4, /* 3520 */
++		0xdc,0xa8,0xd3,0xb2,0xe3,0x84,0x8b,0x4d,0x60,0x46,0xf6,0xe0,0xde,0x3a,0x4f,0x68,
++		0x3f,0x25,0xe0,0x60,0x5e,0x84,0xb3,0x6f,0x48,0x3c,0x40,0x4e,0xf8,0x99,0xcb,0x3f,
++		0xcc,0xbe,0x8c,0xb2,0xa6,0xf0,0xa7,0xe1,0x0b,0x19,0x48,0xcd,0x4f,0x93,0xf1,0x81,
++		0x55,0x5f,0x66,0x1d,0x31,0xd4,0x26,0x80,0x8b,0xbf,0x9f,0x66,0xfd,0x60,0xd6,0x49,
++		0x26,0x9c,0xa3,0xfe,0x99,0x1b,0x22,0x42,0x8c,0x37,0xad,0x2a,0x08,0x68,0x0f,0x74, /* 3600 */
++		0x7c,0xc0,0x36,0x0c,0xcd,0x37,0x3d,0xc6,0xa9,0xf4,0x3a,0x66,0x47,0x0e,0x01,0x4e,
++		0x72,0xb3,0xd8,0xc3,0x8e,0x02,0x04,0x42,0xd8,0xaa,0xb9,0x74,0xe6,0x04,0x93,0x74,
++		0x14,0x5b,0x04,0xcb,0x7f,0x30,0x44,0xaa,0xc1,0xef,0xda,0xb2,0xa1,0x8b,0xb4,0x64,
++		0xd4,0xf2,0xf2,0xd8,0x14,0x39,0x74,0xc9,0x5e,0xee,0x85,0x6d,0x59,0xec,0x00,0x28,
++		0x8e,0xd4,0x3f,0xf5,0xcc,0x88,0x03,0x00,0x6c,0x99,0x55,0x14,0xa2,0xcc,0x9c,0xa6, /* 3680 */
++		0x22,0xb6,0x1b,0xcd,0x75,0xec,0x51,0xc2,0x02,0xa9,0x17,0x10,0x5b,0x4a,0x4b,0xed,
++		0x1b,0x80,0x14,0x68,0x31,0xdc,0xed,0x07,0xef,0xd2,0xed,0x25,0x73,0x9f,0x54,0x09,
++		0x69,0x11,0xb1,0x50,0xd3,0x07,0x7c,0xcd,0x73,0x1a,0x03,0x61,0x68,0x27,0x25,0xd5,
++		0x38,0x03,0xf8,0xfc,0xea,0xa8,0x39,0x19,0x29,0x1e,0xdb,0x44,0x93,0xec,0x84,0xcc,
++		0xe1,0xd0,0xf8,0x2a,0x67,0x92,0x36,0xea,0xd1,0x00,0x2a,0xe8,0x01,0x8c,0xac,0x9f, /* 3760 */
++		0xdb,0xd2,0x46,0xff,0x09,0x3d,0x80,0x3c,0x0d,0xe3,0x32,0x6a,0x57,0x90,0x7b,0x0d,
++		0xd6,0xb0,0x1d,0x08,0x14,0x58,0xc7,0x57,0x28,0xc6,0x00,0x82,0x99,0x28,0x89,0x0a,
++		0x56,0xaa,0xaf,0xef,0xcf,0x74,0x23,0xb7,0x0a,0x6d,0x86,0xb4,0x15,0xb8,0x35,0x8d,
++		0xd0,0x44,0xab,0xee,0x00,0xb9,0xc9,0x79,0x5f,0xc8,0xf6,0x1a,0x64,0x68,0x6d,0xf5,
++		0xf8,0x76,0xa8,0xf3,0x30,0x61,0x59,0x9a,0xe8,0x30,0xf7,0xeb,0x4c,0x4b,0xff,0x87, /* 3840 */
++		0x5f,0x4a,0x93,0x6c,0x40,0x3c,0x5d,0x16,0x0d,0xe5,0xd3,0x3c,0xae,0xe4,0x0f,0xb7,
++		0x18,0xdd,0xa4,0x47,0x8a,0xc6,0xf5,0x1c,0x59,0xc2,0x15,0x52,0x54,0xbd,0x77,0x67,
++		0x11,0x18,0x41,0x1e,0x26,0x09,0xd0,0x00,0x30,0x6f,0xc9,0x50,0x70,0x04,0xa3,0x1e,
++		0x89,0x57,0xea,0x40,0xc2,0x56,0x4b,0x83,0xc3,0xab,0xb7,0x1a,0x87,0xc1,0x1b,0xd1,
++		0x8d,0x78,0x91,0xc4,0x49,0xdb,0xbe,0x79,0xb4,0xa4,0xfb,0x04,0x83,0x07,0xce,0x0e, /* 3920 */
++		0x81,0x2b,0x2c,0x68,0xec,0xab,0x77,0xfd,0x11,0x11,0x52,0x6a,0xb0,0x81,0x73,0x06,
++		0xce,0xbc,0xb0,0x49,0x7c,0x55,0x24,0x31,0xce,0x15,0xe4,0xab,0x52,0x28,0x3f,0x67,
++		0x94,0x80,0xd6,0x9d,0xdd,0xe1,0xf2,0x57,0x9c,0xfd,0xbe,0x0b,0xca,0x95,0xfc,0x5b,
++		0x2d,0xb0,0xc5,0xcc,0x76,0xa3,0x19,0x50,0xf5,0x11,0x6a,0xae,0x5f,0x02,0xd4,0x67,
++		0x10,0xe4,0x25,0x7a,0x75,0xfd,0xed,0xf2,0xf4,0x7c,0xe3,0x7c,0x20,0x3e,0x7f,0x24, /* 4000 */
++		0xd3,0xc9,0x17,0x97,0x13,0xc5,0xd8,0x07,0xc2,0x96,0x14,0x9a,0x75,0xcc,0xb4,0x44,
++		0xf0,0xc6,0xf6,0xab,0xdd,0x2d,0xbb,0x29,0x85,0xfe,0x26,0x74,0x82,0x85,0x8a,0x1e),/* 4032 */
+ 	  chunk_from_chars(
+ 		0x30,0x82,0x07,0xb2,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,
+ 		0x12,0x03,0x82,0x07,0xa1,0x00),
+@@ -993,6 +1422,319 @@ static test_t tests[] = {
+ 	  chunk_from_chars(
+ 		0x38,0x35,0x9f,0xbc,0xd7,0x95,0x82,0xcf,0xfe,0x60,0x9e,0x13,0x7e,0xe2,0xef,0xe8,
+ 		0xa8,0xdb,0xcb,0xad,0x18,0xba,0x92,0xbb,0x43,0x3a,0xb4,0xf0,0x9b,0x49,0x29,0x9d),/*   32 */
++	  chunk_from_chars(
++	  	0x30,0x82,0x13,0x38,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,
++	  	0x03,0x04,0x03,0x13,0x04,0x82,0x13,0x24,0x04,0x82,0x13,0x20),
++	  chunk_from_chars(
++	  	0x30,0x82,0x13,0x5e,0x02,0x01,0x00,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,
++	  	0x03,0x04,0x03,0x13,0x04,0x82,0x13,0x4a,0x30,0x82,0x13,0x46,0x04,0x20),
++	  chunk_from_chars(
++		0x69,0x24,0xbb,0x42,0x57,0xa7,0xb9,0xaf,0xf0,0x95,0xc3,0x0b,0xb3,0x5c,0x6a,0xe4,
++		0x19,0x82,0x63,0x12,0x0f,0x80,0x39,0xaa,0x4e,0x78,0xe1,0x74,0xa7,0x86,0xce,0x00,
++		0x3b,0x9a,0xc2,0xc1,0x42,0x2a,0x1a,0xe8,0x02,0xdd,0xd7,0x46,0x4d,0x3f,0x32,0x72,
++		0x9a,0x3c,0x7d,0xe8,0x94,0xd5,0x06,0xac,0xad,0x25,0xce,0xb3,0x72,0xea,0x31,0x49,
++		0xc9,0x87,0x80,0xdc,0xd1,0x31,0x4b,0xaa,0x29,0xb9,0xb8,0x07,0x75,0x4c,0x47,0xde, /*   80 */
++		0x5d,0xca,0x95,0x40,0x64,0xf2,0x85,0x28,0xb8,0x15,0xfe,0x27,0xb7,0x9a,0xc5,0x06,
++		0xb3,0xad,0x76,0x29,0xd2,0xc9,0x71,0xab,0x8f,0x28,0x2e,0x0c,0x6e,0x7e,0x55,0x48,
++		0xee,0x0e,0x11,0x32,0x42,0xb7,0xa0,0xe0,0x64,0xa6,0xdb,0xce,0x30,0xc5,0x61,0x9b,
++		0x19,0x80,0x08,0x89,0xa0,0x44,0x04,0xb5,0x00,0x13,0xc0,0x88,0xc1,0x30,0x29,0x62,
++		0x12,0x4c,0xd3,0xb4,0x91,0x0a,0x35,0x2c,0x43,0x12,0x31,0x19,0x99,0x65,0x22,0x18, /*  160 */
++		0x52,0x02,0xc3,0x85,0x23,0x44,0x0d,0x90,0x24,0x4a,0x1a,0x30,0x22,0x44,0x28,0x61,
++		0x81,0x06,0x29,0x18,0x97,0x68,0x0a,0x20,0x09,0x08,0x32,0x6a,0x44,0xa4,0x4c,0x44,
++		0x90,0x21,0x8a,0x16,0x68,0x9a,0xa8,0x51,0x1a,0xa5,0x2c,0x62,0x46,0x8d,0x04,0xc3,
++		0x40,0xd3,0x86,0x28,0x60,0xa4,0x60,0x13,0x18,0x70,0x84,0x94,0x8c,0x63,0xc0,0x44,
++		0x04,0xa9,0x28,0x20,0x08,0x20,0x43,0x16,0x2a,0x23,0x29,0x2d,0x1a,0xb1,0x29,0x48, /*  240 */
++		0xb6,0x09,0x21,0x88,0x31,0x00,0xc5,0x30,0x00,0xc4,0x8c,0xd9,0x82,0x68,0xe1,0x30,
++		0x4c,0x63,0x32,0x45,0x0c,0x32,0x86,0x18,0x08,0x31,0x91,0x98,0x0d,0x10,0xb8,0x70,
++		0x9b,0x30,0x22,0x64,0x04,0x08,0x93,0xa4,0x8c,0x21,0xc9,0x70,0x0c,0x35,0x71,0x5b,
++		0x00,0x0d,0x14,0x31,0x22,0xcc,0x98,0x10,0x21,0x04,0x80,0x9b,0x28,0x64,0x1c,0x30,
++		0x80,0x21,0x30,0x71,0x18,0x33,0x50,0x24,0x25,0x44,0x08,0x17,0x8c,0xc0,0x08,0x48, /*  320 */
++		0x84,0x44,0x90,0x48,0x98,0x30,0xca,0x44,0x00,0x09,0x19,0x51,0x19,0x23,0x0c,0x52,
++		0x20,0x0e,0x49,0x06,0x32,0x1c,0x15,0x4e,0x19,0x48,0x85,0x13,0x25,0x49,0xa3,0x00,
++		0x04,0x08,0x15,0x6d,0x20,0x41,0x0c,0xda,0x42,0x52,0xc1,0x34,0x8c,0x00,0x31,0x69,
++		0x43,0x82,0x24,0x64,0x94,0x6d,0x1c,0x81,0x11,0x01,0x96,0x21,0x4b,0x02,0x00,0xca,
++		0x28,0x84,0xcc,0x46,0x64,0x51,0x18,0x6a,0x18,0x10,0x00,0xa4,0x98,0x21,0x60,0xb0, /*  400 */
++		0x68,0x03,0x94,0x6c,0x94,0x48,0x51,0x80,0x40,0x46,0x92,0x22,0x2c,0x23,0x44,0x69,
++		0x98,0x26,0x4d,0x1c,0x01,0x08,0x52,0x02,0x20,0x8a,0xa6,0x08,0x0a,0x31,0x61,0x93,
++		0x40,0x0e,0x9c,0xc8,0x11,0x81,0x32,0x2e,0x21,0x15,0x84,0x84,0xc2,0x41,0x00,0x22,
++		0x72,0x54,0x22,0x62,0x58,0x06,0x92,0x48,0x48,0x44,0x11,0x27,0x04,0x04,0xc0,0x11,
++		0x92,0x82,0x45,0xa1,0xc6,0x8c,0xe3,0x32,0x66,0xc1,0x38,0x72,0x5a,0x86,0x01,0x0c, /*  480 */
++		0xc9,0x90,0x84,0x34,0x08,0x58,0xa8,0x60,0x80,0xc0,0x70,0xd0,0x26,0x62,0x9b,0x30,
++		0x2a,0x04,0x29,0x69,0x04,0x10,0x8d,0x0b,0xb9,0x04,0x50,0x46,0x28,0x50,0x48,0x24,
++		0xd0,0x48,0x05,0xa2,0x48,0x02,0xc3,0x20,0x8c,0xa0,0x14,0x00,0x41,0x38,0x21,0x4b,
++		0x24,0x01,0x04,0xb5,0x49,0x42,0x00,0x00,0x0c,0x24,0x28,0x12,0x40,0x84,0xa2,0x20,
++		0x44,0x9b,0x06,0x90,0x63,0xc0,0x88,0x8c,0x14,0x21,0x49,0x12,0x10,0x54,0x16,0x24, /*  560 */
++		0x20,0x87,0x44,0x50,0x10,0x85,0x0c,0xb5,0x64,0xdb,0x24,0x41,0xd0,0x42,0x29,0x9a,
++		0x16,0x8a,0x21,0xb4,0x4c,0x13,0xb7,0x70,0x10,0xc0,0x85,0x19,0x02,0x69,0xcc,0x40,
++		0x61,0x1c,0x48,0x46,0x98,0x06,0x25,0x60,0x14,0x46,0xe4,0x22,0x62,0x24,0x27,0x22,
++		0x62,0x24,0x29,0x44,0xc6,0x2d,0x08,0x31,0x84,0x20,0x32,0x21,0x04,0xb4,0x61,0x0a,
++		0x38,0x12,0xd9,0x28,0x44,0xa4,0x08,0x20,0xcc,0xa8,0x29,0x0b,0x21,0x31,0x0a,0x34, /*  640 */
++		0x29,0x03,0x21,0x40,0xc1,0xa2,0x6c,0x8a,0x16,0x12,0x52,0xa6,0x64,0xa3,0xb2,0x51,
++		0x04,0x29,0x51,0xc4,0x04,0x91,0x63,0xb0,0x2d,0x14,0x44,0x30,0x8c,0x40,0x66,0x0c,
++		0x40,0x0c,0x01,0xa5,0x2c,0x09,0x94,0x2d,0x62,0xc6,0x11,0x03,0x98,0x50,0x19,0x10,
++		0x4d,0x19,0xa8,0x28,0xd3,0x86,0x40,0xc0,0x30,0x65,0x54,0xa6,0x71,0xe0,0xb4,0x85,
++		0x9b,0x86,0x10,0x04,0x36,0x69,0xd0,0x46,0x29,0x18,0xa3,0x71,0x40,0x22,0x49,0x00, /*  720 */
++		0x43,0x85,0xcb,0x40,0x28,0x89,0x36,0x66,0x41,0x22,0x69,0xa4,0x28,0x51,0xd9,0x80,
++		0x29,0x14,0x07,0x21,0xda,0x80,0x91,0x1b,0x26,0x50,0x5b,0xa0,0x60,0x99,0x42,0x71,
++		0x50,0x88,0x49,0x10,0x23,0x09,0x5a,0x90,0x21,0x22,0x27,0x8e,0x43,0xb2,0x70,0x0c,
++		0xc9,0x4c,0xa4,0x02,0x70,0x92,0x40,0x10,0x0a,0x39,0x70,0x02,0x36,0x0e,0x11,0x30,
++		0x41,0xd8,0x40,0x2d,0x1b,0x24,0x6d,0xc3,0x92,0x61,0x4c,0x86,0x8d,0x21,0xb8,0x00, /*  800 */
++		0xd3,0x24,0x22,0x12,0xc8,0x21,0x12,0x99,0x85,0x09,0x16,0x0c,0x5a,0xa2,0x24,0x09,
++		0x34,0x42,0x10,0xa2,0x24,0x03,0x42,0x8c,0xc0,0xb2,0x8d,0x12,0xb6,0x69,0x63,0x34,
++		0x0d,0xcc,0xb0,0x65,0xa1,0x12,0x11,0x4a,0x38,0x69,0xcc,0x14,0x81,0x58,0x44,0x09,
++		0x54,0xa6,0x80,0x0c,0xa8,0x05,0xc4,0x38,0x8a,0x84,0x06,0x01,0x9b,0x32,0x2d,0x83,
++		0x12,0x90,0x09,0x02,0x60,0xa1,0x28,0x88,0x58,0x10,0x41,0x24,0x40,0x02,0x19,0x34, /*  880 */
++		0x48,0x18,0xa0,0x4d,0x00,0x10,0x62,0x13,0x22,0x50,0xe3,0x38,0x21,0x9a,0x96,0x21,
++		0x53,0x08,0x80,0x51,0x26,0x01,0x99,0xc4,0x28,0x1b,0xb9,0x71,0x04,0x97,0x84,0x04,
++		0x05,0x2c,0xa0,0xc2,0x10,0xd3,0x42,0x81,0x81,0x42,0x4d,0x61,0x84,0x6c,0x5a,0x30,
++		0x49,0x1b,0xc2,0x24,0xc0,0x20,0x28,0xca,0x92,0x2d,0x4a,0x90,0x10,0x04,0x27,0x86,
++		0x4c,0x96,0x21,0x09,0x19,0x45,0x14,0x82,0x2c,0x11,0xa6,0x91,0x13,0xb8,0x04,0x03, /*  960 */
++		0x18,0x70,0x01,0xa2,0x51,0x52,0x14,0x49,0x5a,0x02,0x30,0xcb,0x30,0x2c,0x94,0x10,
++		0x2c,0x00,0x49,0x86,0x09,0xa0,0x25,0xc2,0x12,0x4c,0x1b,0x02,0x69,0x40,0xa4,0x44,
++		0x41,0x16,0x62,0x02,0x25,0x28,0xdc,0xa2,0x2d,0x00,0x16,0x42,0x58,0x30,0x66,0x5b,
++		0x86,0x24,0xd4,0x24,0x48,0xdb,0x26,0x0c,0x4c,0x08,0x85,0x01,0x90,0x49,0x21,0x24,
++		0x41,0x54,0x06,0x84,0x02,0x43,0x4a,0x24,0x42,0x05,0x40,0x14,0x48,0xca,0x44,0x84, /* 1040 */
++		0xc0,0x42,0x0c,0x98,0x26,0x04,0x9c,0xa2,0x05,0xd1,0xc2,0x51,0x13,0x01,0x86,0x1c,
++		0xa1,0x50,0xd9,0x02,0x50,0x0c,0x39,0x86,0x8c,0x00,0x31,0x22,0x05,0x48,0xd3,0x10,
++		0x81,0x12,0x48,0x05,0xd1,0x08,0x69,0x62,0x38,0x2c,0x0a,0x23,0x70,0x9b,0x44,0x72,
++		0xe3,0x48,0x6e,0x22,0x96,0x70,0x14,0x33,0x6c,0xd8,0x90,0x29,0x03,0x00,0x49,0x63,
++		0x20,0x8a,0x03,0x91,0x25,0x08,0x89,0x21,0xc0,0x82,0x0c,0x99,0x40,0x32,0xc2,0x34, /* 1120 */
++		0x4e,0x4b,0x98,0x69,0x09,0x80,0x44,0xe4,0x04,0x69,0x94,0x20,0x09,0x99,0x24,0x6d,
++		0x09,0xa9,0x60,0x01,0x29,0x2d,0xc8,0x42,0x28,0x8a,0x34,0x02,0xe4,0x08,0x70,0x0c,
++		0x23,0x6e,0x0a,0x05,0x49,0x64,0x44,0x2a,0x82,0xc8,0x00,0x02,0x48,0x31,0xcb,0x90,
++		0x50,0x1c,0x05,0x68,0x12,0x12,0x2c,0xd0,0x80,0x0c,0x59,0x48,0x61,0xcb,0xa6,0x09,
++		0x9c,0xc0,0x81,0x42,0xb8,0x00,0x24,0x41,0x8a,0x94,0x20,0x40,0x42,0x14,0x4d,0x19, /* 1200 */
++		0x46,0x62,0x18,0x05,0x09,0x24,0x33,0x6a,0xd4,0x00,0x61,0x12,0x48,0x32,0x8a,0x04,
++		0x72,0x93,0xb4,0x69,0x62,0xc2,0x71,0x41,0xa6,0x89,0x44,0x96,0x31,0x62,0x30,0x46,
++		0x83,0x42,0x6c,0x00,0x19,0x22,0x09,0x46,0x4d,0x8b,0x06,0x49,0xe1,0xb0,0x70,0x42,
++		0x44,0x31,0xc1,0x80,0x65,0x9c,0x00,0x24,0x11,0xa8,0x31,0x13,0x21,0x2c,0x4b,0x46,
++		0x28,0x1b,0x18,0x0d,0x88,0x42,0x70,0xd1,0xb0,0x0d,0x90,0xc8,0x45,0xda,0xc2,0x48, /* 1280 */
++		0x59,0x14,0x26,0x22,0x44,0x00,0xc2,0x94,0x41,0x50,0xc8,0x04,0x18,0x00,0x00,0xcb,
++		0xa6,0x24,0x19,0x02,0x10,0x10,0x89,0x0c,0x18,0x22,0x21,0x62,0xa8,0x81,0xc8,0x92,
++		0x48,0xd3,0x94,0x20,0x82,0x06,0x72,0x09,0xa8,0x90,0x0c,0x49,0x8a,0x41,0x86,0x28,
++		0x19,0xc5,0x80,0x9a,0x18,0x4d,0x14,0x10,0x2e,0x22,0x12,0x52,0x00,0x08,0x12,0x0c,
++		0x33,0x45,0x63,0xc6,0x30,0x10,0x93,0x4c,0x60,0xc6,0x31,0xdc,0x40,0x0e,0x98,0x82, /* 1360 */
++		0x50,0x60,0x02,0x2a,0xd2,0x22,0x40,0xe4,0x06,0x2d,0xdb,0x32,0x0e,0xca,0x32,0x4e,
++		0xd4,0x18,0x24,0x08,0xc3,0x28,0x4a,0xc2,0x68,0xe2,0x80,0x40,0xa1,0xc8,0x64,0x51,
++		0xc2,0x65,0xcb,0x16,0x60,0x23,0x09,0x4c,0x82,0x04,0x68,0xd9,0x22,0x2e,0x1c,0x49,
++		0x92,0x42,0x24,0x21,0x00,0x37,0x0e,0xc8,0x12,0x72,0x64,0x08,0x25,0x0a,0x20,0x2a,
++		0x58,0x24,0x04,0x59,0x16,0x4c,0x08,0x17,0x30,0x00,0x46,0x05,0x12,0x90,0x40,0x03, /* 1440 */
++		0x07,0x21,0x52,0xc0,0x64,0x1c,0x83,0x6d,0x9c,0x32,0x2e,0x11,0x15,0x8a,0x10,0x35,
++		0x88,0x5a,0xa0,0x8d,0xd9,0x80,0x48,0x03,0xb6,0x4c,0x01,0x10,0x65,0x10,0x86,0x40,
++		0x11,0x01,0x42,0x0a,0xc1,0x64,0xdb,0x22,0x4d,0x64,0xb2,0x51,0x02,0x36,0x0d,0x93,
++		0x46,0x31,0x14,0xb6,0x68,0x63,0x84,0x29,0xc8,0x10,0x24,0x94,0x30,0x08,0x19,0x37,
++		0x02,0x14,0x82,0x45,0x88,0x28,0x40,0x54,0xa8,0x29,0x90,0x14,0x12,0x61,0x36,0x12, /* 1520 */
++		0x0b,0x09,0x8c,0xa4,0x98,0x28,0xc2,0x92,0x45,0x4c,0x00,0x60,0x63,0xc4,0x81,0xc0,
++		0x36,0x25,0xca,0x88,0x2d,0x24,0x40,0x30,0xd3,0xa8,0x2d,0xc9,0xc8,0x25,0xd2,0x84,
++		0x48,0x00,0x32,0x92,0x50,0xa2,0x71,0xd3,0x44,0x0d,0x22,0x34,0x60,0x12,0x13,0x12,
++		0x86,0x8c,0x5f,0x86,0x20,0x79,0x4a,0x05,0x0e,0x20,0xd0,0xe1,0x01,0x17,0x86,0x24,
++		0x0e,0xa6,0x64,0xf2,0xf6,0x9b,0xb1,0xb7,0xe3,0x0e,0xc6,0x6b,0x1a,0x4a,0x0b,0xe5, /* 1600 */
++		0x9b,0x79,0xf2,0x19,0x8a,0xd9,0x80,0x44,0x83,0xe4,0x75,0xe5,0x3b,0x3c,0x49,0xcb,
++		0x0c,0xe5,0xef,0x92,0x91,0x2a,0xf4,0x40,0xf2,0x3b,0x99,0x58,0x13,0xd1,0x1b,0x59,
++		0xf7,0x98,0xe9,0x3c,0x9d,0x13,0x53,0x98,0x17,0xc7,0xac,0x68,0xca,0xd1,0xaa,0x1a,
++		0xc2,0x76,0x56,0xbd,0x0c,0x47,0x97,0xe9,0xc8,0xec,0x17,0x78,0x4c,0x1a,0x32,0x7a,
++		0x9d,0xfe,0xaf,0x4d,0x61,0x91,0xee,0xcd,0xaf,0xe0,0x49,0xb7,0x33,0xfe,0x39,0xd5, /* 1680 */
++		0xeb,0x40,0x00,0x93,0x6f,0xee,0xfc,0xf8,0x29,0x28,0xe9,0xf9,0x4c,0xfd,0x5c,0xf4,
++		0xc1,0xe3,0xde,0xb1,0x43,0x3a,0x47,0xf6,0xd3,0x28,0xb5,0xe8,0x3d,0xd1,0x56,0xd0,
++		0x18,0x2d,0xc6,0x92,0x34,0x75,0x91,0xaa,0x6f,0x73,0x2c,0xfb,0xe9,0x82,0x93,0x5f,
++		0xd1,0x84,0x6c,0xac,0xf4,0xcb,0x85,0x15,0xc5,0x5a,0xb8,0x5e,0xe5,0xad,0x44,0xcb,
++		0x09,0xd3,0x26,0x9e,0x2e,0x6d,0x11,0x78,0x09,0x61,0xfd,0x13,0x1d,0x5e,0x6f,0xbf, /* 1760 */
++		0x89,0x84,0x9f,0x47,0xf2,0xb7,0x1d,0x82,0x83,0xff,0x25,0x38,0x5e,0x52,0xb0,0x7d,
++		0xbb,0x26,0x6c,0x67,0x4c,0xee,0x3d,0x0b,0x5d,0xf5,0xa5,0x6d,0x8b,0xdc,0xdc,0xfa,
++		0xae,0xe6,0xa2,0x48,0xe7,0x1d,0xb1,0x34,0x5a,0xfc,0x59,0x7c,0xa8,0x30,0xa1,0xa3,
++		0x5b,0x43,0x96,0xef,0x4c,0x1a,0xdf,0x9e,0xd0,0x1b,0xce,0x9b,0x6e,0xb6,0x37,0xfa,
++		0x24,0xaa,0x16,0x0b,0x90,0x76,0xba,0xe3,0x05,0x59,0xf8,0xb2,0x9d,0xed,0xb3,0xd2, /* 1840 */
++		0x5b,0x79,0x06,0x4a,0xb0,0xcf,0x8b,0x8d,0x70,0xad,0xdd,0xeb,0x8b,0x17,0x42,0x48,
++		0xd5,0xae,0xa4,0xd1,0x8d,0xe4,0x3b,0x89,0x38,0xcd,0xd2,0xac,0xba,0x54,0x77,0xbd,
++		0x4a,0xac,0xc3,0xce,0x59,0x5e,0x5d,0x26,0x9f,0xe6,0x75,0x21,0x0d,0x23,0x15,0x2b,
++		0x04,0x71,0x0f,0x36,0x84,0x28,0x79,0x4a,0x75,0xf4,0x9b,0x68,0x3e,0xd2,0x0d,0xd6,
++		0x47,0x51,0x57,0x77,0x95,0x5a,0x8c,0xb3,0x8a,0x36,0xaf,0xcd,0x2c,0xe0,0xac,0xec, /* 1920 */
++		0x4f,0x0d,0xfe,0x80,0x77,0x02,0xd1,0xeb,0x3b,0xde,0x72,0xe9,0xe0,0x85,0xaa,0x4e,
++		0x09,0xeb,0x1b,0x09,0x47,0x41,0x38,0x52,0xec,0x3c,0x0a,0xc5,0x2f,0x06,0xcb,0x95,
++		0x9c,0x85,0x39,0x4e,0xb3,0x74,0x81,0x19,0xed,0xbe,0x6c,0x80,0xd2,0xd8,0xf7,0x92,
++		0xce,0x0d,0x91,0x5e,0x4f,0x4b,0x15,0x1e,0xfb,0x13,0x5e,0x7f,0x4d,0xc9,0x7d,0x85,
++		0x81,0x41,0xc5,0x7f,0x70,0x41,0x7b,0x43,0xa6,0xa1,0x26,0x95,0x69,0x78,0xd7,0x8e, /* 2000 */
++		0xfb,0x9f,0x03,0x72,0x43,0xb4,0xcb,0x41,0xdf,0x96,0x8b,0x7e,0xe5,0xb5,0x20,0x87,
++		0xf0,0x5a,0xa9,0xfe,0x48,0x7b,0xd1,0x6c,0x03,0x47,0xcf,0x13,0x35,0x76,0x0b,0xd2,
++		0x39,0x8a,0xd5,0x4d,0xda,0x00,0xa5,0xaa,0xc4,0x46,0xd8,0x0b,0x1c,0x79,0x98,0xc6,
++		0x02,0x19,0x2a,0xda,0xfc,0xb8,0x09,0xd1,0x4e,0xe3,0x28,0x64,0x1b,0xa3,0xaa,0x00,
++		0xf8,0xd2,0x9c,0x3a,0x84,0x8a,0xcb,0xdc,0x19,0x46,0xbc,0x0d,0x35,0xe0,0xbe,0x0f, /* 2080 */
++		0x8f,0x7e,0x3d,0xa3,0xf6,0x8d,0x9f,0xa9,0x76,0x8f,0x5c,0xf2,0x75,0x53,0x4a,0x0e,
++		0xca,0x9e,0x60,0xfc,0xea,0x38,0xf1,0xe0,0x42,0xc3,0x16,0x14,0x3a,0x76,0x7b,0x33,
++		0xac,0xca,0xd8,0xc8,0xd6,0x6c,0x70,0xc7,0x5f,0xd1,0xf0,0xb2,0x58,0x6b,0x65,0x3a,
++		0xd4,0xaf,0x54,0xe5,0x6e,0xf0,0x69,0x33,0xea,0xd3,0x1d,0xe3,0x65,0xd1,0x10,0xb9,
++		0xc4,0xa2,0xa9,0x8b,0xcb,0xa1,0x65,0xca,0xfe,0x38,0x6f,0x88,0x7c,0x72,0x15,0x6e, /* 2160 */
++		0xb1,0x4f,0xf0,0xda,0xd6,0x65,0x61,0x6c,0xe3,0xce,0x65,0xc1,0x90,0x4f,0x2c,0x17,
++		0x47,0xb2,0xec,0x2b,0x5c,0x9d,0x67,0x76,0xbc,0xd7,0x9e,0x5a,0xc6,0x4b,0x79,0x33,
++		0xbd,0xde,0xde,0xdd,0xbb,0xc7,0x25,0xbf,0xdb,0xcc,0xde,0x2f,0xb3,0x75,0xae,0x2b,
++		0xe3,0x53,0x7b,0xdf,0x89,0xbf,0x4c,0x25,0xf8,0x3a,0x49,0xd6,0xa6,0xa8,0xd0,0x76,
++		0x1c,0xf3,0x9d,0x62,0x0c,0x53,0xed,0x83,0x7d,0x19,0x82,0x55,0xcf,0x5b,0x91,0x0a, /* 2240 */
++		0x6d,0xb5,0x78,0x77,0xdf,0x92,0xd8,0xbb,0x6e,0x9c,0x52,0x6b,0x8c,0x4e,0xc9,0x31,
++		0x00,0xde,0xe0,0x50,0x0a,0x21,0x0c,0x98,0x45,0x83,0xe1,0x53,0x81,0x60,0xed,0xac,
++		0x2c,0x6f,0x86,0x6e,0x7f,0x5d,0x99,0xd7,0xb1,0xb8,0x15,0x82,0xf5,0xd0,0xeb,0xbf,
++		0x27,0x86,0xe3,0xf5,0x56,0x01,0x3b,0xa9,0xb6,0xf6,0x56,0xeb,0x79,0x88,0x38,0xea,
++		0x05,0x79,0x20,0x1a,0x95,0xd5,0x6b,0xbc,0x3b,0xcd,0xb9,0x51,0x1a,0xfb,0xd4,0xd8, /* 2320 */
++		0x12,0x88,0x89,0x6f,0x87,0x10,0x8c,0x07,0x7f,0x1a,0x81,0xa3,0xbd,0x29,0x7b,0xb1,
++		0x24,0xa8,0x00,0x86,0x89,0x02,0x42,0x99,0x5e,0x03,0xcf,0x42,0xa0,0xc2,0x1e,0x27,
++		0x2a,0x9a,0xfa,0x1d,0xc1,0x03,0x46,0x3d,0x2a,0xb4,0x94,0xf7,0xd0,0x17,0x68,0x6d,
++		0x31,0x89,0x4d,0xd2,0xf6,0xeb,0xb0,0xc3,0xcb,0x62,0x23,0xec,0x79,0xc6,0x5d,0x45,
++		0xc1,0xb0,0xd4,0xef,0x19,0x61,0xf1,0x6d,0x65,0x3f,0xcf,0x25,0x97,0x7b,0x65,0x1e, /* 2400 */
++		0xc5,0x1a,0x13,0xae,0x8d,0x4a,0x34,0x72,0xee,0x71,0x96,0x9a,0x7a,0x93,0x6f,0x5d,
++		0xbb,0xb9,0x39,0x6a,0x46,0xd9,0x76,0x42,0x35,0x8c,0xaf,0x48,0x94,0xc9,0xa6,0xdf,
++		0x84,0xa5,0x9c,0x59,0x62,0xa6,0x99,0x0a,0x76,0xf0,0x61,0x48,0x90,0x16,0x9f,0x00,
++		0x18,0x70,0xd4,0x9c,0xf2,0xe7,0x50,0x08,0xcc,0x4a,0x5d,0x85,0xe7,0x2d,0xe2,0xd6,
++		0xcf,0x3f,0xa7,0x18,0x52,0x25,0x35,0x22,0xfe,0x8b,0x0e,0x42,0x3c,0xb4,0x17,0xa3, /* 2480 */
++		0x8e,0xb7,0x8c,0x87,0x63,0xc3,0x72,0x0c,0x04,0xe6,0x7f,0xf8,0x89,0x79,0xeb,0xa0,
++		0x9e,0x34,0x53,0x8b,0xb5,0x23,0xb9,0x9b,0x8e,0x34,0x16,0x74,0x12,0xf7,0x7a,0xea,
++		0x89,0x4d,0x83,0xac,0xf9,0x46,0xfc,0x05,0x4d,0x0a,0xf4,0x72,0x95,0xe5,0x1e,0xd8,
++		0x3f,0x74,0x86,0x94,0x0a,0x4d,0x41,0xc0,0x4a,0xd7,0xeb,0xee,0x61,0x0b,0xf1,0xd0,
++		0x3f,0xa5,0x40,0x71,0xd5,0x1a,0x15,0x09,0xe4,0xf4,0x91,0x63,0xa2,0x50,0x81,0xbe, /* 2560 */
++		0x87,0x90,0xd0,0x87,0xf5,0xf4,0xf0,0x5c,0x88,0x55,0x0f,0xca,0x9b,0xf9,0x9c,0x9b,
++		0xe5,0x95,0x3d,0x51,0xdd,0x08,0x45,0xc9,0x3e,0x41,0xee,0xef,0x62,0xe0,0x79,0x4b,
++		0x29,0x27,0xc4,0xf5,0xed,0x9b,0xd3,0xe3,0x4e,0xa9,0x20,0x0a,0x79,0xdd,0xeb,0x4b,
++		0x2d,0x8f,0x30,0x5f,0xe0,0x5f,0x82,0x7c,0x7e,0x2e,0xd1,0x86,0x34,0x1c,0xb5,0xd1,
++		0x15,0x2f,0xc8,0x01,0x04,0xe0,0xe1,0x36,0x83,0xd9,0x41,0x29,0x4c,0x77,0x84,0x17, /* 2640 */
++		0x16,0x4b,0x68,0x4a,0x97,0x6e,0x56,0xe7,0x8d,0xa4,0xd1,0x7c,0x3c,0x73,0x22,0x93,
++		0x14,0x87,0x0b,0x85,0xc4,0x55,0xc2,0x3b,0x83,0x0b,0x9a,0x28,0xa3,0xd8,0xc0,0xb5,
++		0x66,0x42,0x6d,0xc1,0x69,0xf3,0x26,0xab,0xce,0x2e,0xff,0xf3,0x9e,0x9b,0x19,0x9a,
++		0xe5,0xc1,0x29,0x2b,0x6f,0x2e,0xf3,0x7a,0xf1,0xde,0xa9,0x27,0x2c,0x8d,0x54,0x23,
++		0xdf,0x8a,0x56,0x32,0xf9,0x91,0xe1,0x4d,0xca,0x25,0x14,0x78,0x8b,0x62,0xbe,0x16, /* 2720 */
++		0x48,0x28,0xe9,0xac,0xb8,0x93,0xdd,0xa6,0x02,0xa5,0xe2,0xfb,0x9e,0xfc,0xbe,0xfd,
++		0x95,0xab,0xfb,0x82,0xd2,0xb0,0x2d,0x49,0xcc,0x53,0x08,0x4a,0x49,0xab,0x1b,0xec,
++		0x23,0xe5,0xb4,0xc8,0xe7,0x14,0xcb,0x03,0x40,0x5f,0x1b,0xcf,0x7e,0x11,0xbb,0x59,
++		0x72,0x9d,0xdc,0x0b,0x7b,0xef,0xb2,0x91,0x27,0x6d,0xce,0xda,0xca,0xad,0x39,0xa2,
++		0xf0,0x1c,0x7d,0xc9,0x8b,0x9e,0x06,0x5e,0xaf,0xed,0x1c,0xc8,0xce,0x3e,0x84,0x80, /* 2800 */
++		0x80,0xa2,0xfc,0x5b,0x98,0xc9,0xf6,0xbf,0x50,0x40,0x27,0x33,0x42,0xf0,0x31,0x2f,
++		0x8b,0x98,0x44,0x59,0x4a,0x50,0x3d,0xd3,0xe6,0xaf,0x1c,0x9e,0x35,0xc1,0x03,0x2a,
++		0x4a,0x8a,0x5e,0x7b,0xf3,0x3a,0x82,0xf3,0x5e,0x16,0xed,0xf8,0xc6,0x0c,0x90,0x02,
++		0x1d,0x8c,0x0b,0xa4,0xc3,0x86,0x24,0x5d,0xfe,0xf0,0x94,0x48,0x43,0x1d,0x8c,0x00,
++		0xd1,0xe2,0x6e,0xe4,0xd8,0xc7,0x7d,0xaa,0x1a,0x70,0x5e,0xd4,0x79,0x2a,0xcb,0x4e, /* 2880 */
++		0xa2,0x7c,0x15,0x66,0xfb,0x56,0x68,0x3c,0x43,0xbf,0x67,0x84,0x2e,0x67,0x53,0x4c,
++		0xb3,0xf9,0x67,0x7c,0x8a,0xb9,0xd0,0xee,0xe7,0x82,0x7c,0xde,0xfc,0x22,0x3a,0xc9,
++		0x48,0xb8,0x80,0xb5,0xf1,0xce,0x95,0x37,0x27,0x29,0x32,0x00,0x2c,0x1a,0x4d,0xd2,
++		0x18,0xf5,0x27,0x16,0x6e,0xbf,0xb2,0xb2,0xfa,0x2b,0xf3,0x72,0x46,0xec,0xdf,0xdf,
++		0xa7,0x2b,0x6d,0xa1,0x1c,0x30,0xd1,0xc7,0xd2,0x48,0xad,0x64,0x81,0x8f,0x69,0x1d, /* 2960 */
++		0x59,0xb7,0x55,0xda,0xf7,0x1b,0xed,0x9a,0xb5,0xfb,0x52,0xe0,0x36,0x22,0xa9,0x00,
++		0xd6,0x6b,0x4c,0x63,0x84,0x16,0x9b,0xdf,0x9e,0xb6,0x1c,0x02,0xdf,0x45,0xfb,0x76,
++		0xb1,0xa2,0x6f,0x34,0xe9,0x38,0xb1,0x90,0x86,0x17,0x45,0xc0,0x21,0xfa,0x87,0x62,
++		0x00,0xc7,0xfc,0x8e,0x22,0x2d,0xdb,0xfa,0xd8,0xbe,0x78,0x1b,0x18,0x54,0x24,0xaa,
++		0xaf,0xc6,0x58,0x62,0xdb,0x13,0x2b,0xec,0x6d,0x18,0x83,0x7a,0x1f,0x58,0xa8,0x76, /* 3040 */
++		0xc9,0x9e,0x63,0xf5,0x14,0x20,0xb8,0x3f,0x45,0x96,0x75,0x61,0x2f,0x7a,0xcf,0x80,
++		0xb4,0xeb,0x1d,0xd0,0x72,0x1c,0xaa,0x1b,0x49,0x70,0xda,0x60,0x86,0x79,0xc6,0x38,
++		0x3e,0x81,0x7f,0xe1,0x6b,0x66,0xb1,0x91,0x81,0xed,0xfc,0x39,0x27,0x0c,0x7e,0x91,
++		0x7b,0x1f,0x10,0xeb,0x7a,0x01,0x19,0x97,0xe9,0x67,0x85,0x3b,0x78,0xe0,0x0c,0xfd,
++		0x58,0xd2,0x24,0xd9,0x33,0xcc,0x5a,0x99,0x55,0x32,0xdc,0xd4,0xe5,0x32,0xe4,0x03, /* 3120 */
++		0x05,0x15,0xf4,0xa0,0x5b,0x33,0x1d,0x57,0x5d,0xda,0xc2,0x9b,0xab,0x06,0x9f,0x09,
++		0xaf,0x0d,0x17,0x33,0x73,0xdb,0x1e,0xc2,0xb6,0x36,0x6b,0xb3,0x71,0x00,0x8a,0x23,
++		0x86,0xfd,0x88,0xbe,0x77,0xf5,0xed,0x5e,0x19,0x8c,0xbe,0x88,0xdf,0x24,0xbc,0x6e,
++		0x39,0x3f,0xeb,0xc1,0x0c,0x47,0x0a,0x72,0xd4,0x7c,0x0f,0x83,0x46,0x53,0xc9,0xae,
++		0x80,0x0e,0x89,0x3c,0x6b,0xa6,0x8e,0xa2,0x8a,0x83,0x8f,0xcb,0xb6,0x9c,0x3e,0x96, /* 3200 */
++		0x4a,0x5f,0xaf,0xc2,0x06,0x7d,0xd4,0x06,0xb2,0x57,0xc9,0x8d,0xd3,0x97,0x9e,0xc7,
++		0xc7,0xec,0xbe,0x96,0xa3,0x3d,0x85,0x51,0x5d,0xa2,0xcb,0x6a,0xa5,0xe1,0xff,0xf2,
++		0x04,0xaf,0x62,0xdd,0x41,0x19,0xa0,0xe4,0x8c,0x04,0xa3,0xf2,0xb3,0x86,0x60,0xf5,
++		0x29,0x64,0xd8,0xd4,0xae,0xe1,0x46,0xa9,0xc5,0x3c,0x31,0x90,0x6d,0xad,0x0f,0xd9,
++		0x0b,0x5d,0x83,0xb3,0xe3,0x1b,0x69,0x0a,0x4c,0x49,0x35,0x24,0x99,0x81,0xbe,0x1f, /* 3280 */
++		0x1a,0x85,0xec,0x6e,0x0f,0xee,0x4c,0x88,0xf2,0xd8,0x9e,0x29,0x69,0xab,0x8c,0xbb,
++		0xeb,0x50,0x19,0x16,0x55,0x8d,0x29,0xea,0x7c,0x3e,0xcf,0x1c,0x9e,0xf1,0xa0,0x43,
++		0x50,0x63,0x3b,0x4c,0xda,0x73,0x7d,0xfb,0x15,0x1c,0xb5,0xe7,0x36,0x11,0x73,0xf3,
++		0xae,0xdd,0xdf,0x52,0x7d,0x73,0xf2,0xf9,0xd5,0xb6,0x21,0x3a,0xa6,0x8f,0x88,0x3e,
++		0x9a,0x26,0x33,0x78,0x5e,0xc6,0xbe,0x64,0x2a,0x9f,0xd0,0xf2,0x1a,0x42,0xf6,0xb9, /* 3360 */
++		0xda,0xab,0xdc,0xd1,0xe6,0xad,0xbe,0xf6,0x48,0x41,0xb5,0x96,0x86,0xea,0xe3,0xec,
++		0x88,0xee,0xf0,0xa9,0xcb,0xc1,0x2b,0xc0,0x12,0x62,0x2d,0xf2,0xdd,0x93,0xa8,0x62,
++		0x29,0x04,0x4a,0xf2,0xf2,0x60,0xd2,0x18,0x3f,0x51,0xe8,0x33,0xee,0x92,0xd9,0x8f,
++		0x02,0x51,0xe3,0xf8,0x5f,0xab,0x74,0xce,0x36,0x7b,0x8b,0x7a,0xa6,0x3d,0x3c,0xf8,
++		0xc8,0xbf,0x4d,0x78,0x35,0x8b,0xae,0x0a,0x02,0x41,0xe2,0x10,0xac,0x69,0x35,0x30, /* 3440 */
++		0x87,0xcc,0x73,0x31,0x35,0x7e,0xb4,0x45,0x0f,0x95,0x09,0xcf,0xe5,0x95,0xf5,0x40,
++		0x32,0xee,0x05,0x77,0x54,0xa8,0xed,0xd7,0x46,0xcb,0x92,0x82,0xe7,0x68,0xdc,0x6b,
++		0x83,0x0c,0x5b,0x4a,0x21,0x93,0x43,0xad,0x12,0x4e,0xdb,0x3b,0xbc,0x42,0x50,0x55,
++		0x66,0xa7,0x03,0x8c,0x95,0x9b,0xc3,0x55,0x85,0xb6,0x05,0x5f,0x19,0x68,0xda,0x24,
++		0x3f,0x77,0x8f,0x4e,0x46,0xdb,0x46,0x2a,0xbe,0xb9,0x3b,0x81,0x24,0x3c,0x31,0xeb, /* 3520 */
++		0x59,0x62,0x2e,0xdf,0x81,0xf0,0x6c,0xcc,0x61,0xd2,0xa6,0xea,0x73,0xe1,0x09,0xc3,
++		0x87,0x91,0x5f,0x27,0x7b,0xcf,0x1f,0xc1,0x11,0x05,0xbb,0xa7,0x02,0x93,0xc0,0xfa,
++		0xb5,0xc0,0x65,0xf2,0x3b,0xaa,0x19,0x29,0x0a,0x30,0x2f,0x08,0x09,0x11,0x07,0xa4,
++		0xb1,0xd5,0x68,0x85,0x26,0x22,0x09,0x83,0x83,0x42,0x77,0x60,0xef,0x8f,0x29,0x28,
++		0x62,0x5b,0xdd,0xa5,0xf5,0x14,0xc5,0xad,0xe9,0x59,0x89,0x1e,0xf2,0x95,0x9f,0x24, /* 3600 */
++		0x8a,0x35,0x32,0xbf,0x9d,0x30,0xe7,0x14,0x05,0x9e,0xbd,0xec,0x95,0x87,0x08,0xf8,
++		0xa8,0x3c,0x26,0x8b,0xef,0x26,0x82,0xd6,0x03,0xca,0x88,0x63,0x47,0xe1,0x98,0xfd,
++		0x68,0x23,0x39,0x99,0xc7,0x7d,0x30,0xd7,0x45,0x5d,0xe6,0xbc,0xfd,0x01,0x44,0x27,
++		0x70,0x62,0xb3,0x04,0xbe,0xf0,0xe3,0x4c,0x5a,0x9d,0x8d,0x78,0x0d,0x29,0xec,0x23,
++		0x21,0xe0,0x73,0x40,0x77,0x1c,0x46,0x36,0x04,0x83,0xad,0xca,0xf1,0x2d,0x5b,0x79, /* 3680 */
++		0xfd,0xbf,0xe2,0x85,0x6a,0xce,0x88,0x59,0xf6,0xb1,0x24,0x14,0xb3,0xf7,0xe8,0xbb,
++		0x58,0x13,0x49,0x89,0x60,0xf3,0x4f,0xdc,0x64,0xfc,0x84,0x85,0x79,0xca,0xf9,0xdc,
++		0xcf,0x19,0xb4,0xfb,0x82,0x5e,0xd5,0x71,0x6d,0xcc,0xcd,0x68,0x72,0xcb,0xde,0x38,
++		0x31,0xd6,0x73,0x84,0x94,0x2c,0xd8,0xa9,0xec,0x4b,0xbf,0xef,0x57,0x06,0xb8,0xf9,
++		0xf0,0x5f,0xe1,0xe8,0xfe,0x69,0xd3,0xea,0x6a,0x86,0x21,0xc2,0x21,0x44,0x17,0x7b, /* 3760 */
++		0x1c,0x12,0x59,0xe1,0xa7,0x9d,0xfd,0xf8,0x97,0x28,0x88,0x7b,0xef,0x1a,0x70,0x48,
++		0x25,0x56,0x83,0x1b,0x67,0x24,0x40,0xe1,0x3f,0xe3,0xe3,0xfc,0x82,0x04,0xa0,0x2e,
++		0xa1,0xef,0xf1,0x9d,0x95,0x25,0x38,0x87,0x28,0x5b,0xfb,0xea,0x16,0xa0,0xf2,0x19,
++		0xef,0xbc,0xec,0x30,0xa8,0xae,0x86,0x58,0x9a,0x57,0x03,0x10,0x3a,0x8a,0x39,0x3f,
++		0xa6,0xf6,0xb6,0x57,0x70,0x4a,0xc6,0x77,0xc1,0x4c,0xd1,0x0d,0x3d,0x62,0xd1,0x3f, /* 3840 */
++		0xbd,0x37,0x8c,0x2d,0xda,0x32,0x5b,0x61,0xb8,0x59,0x52,0xd5,0x12,0x93,0x87,0x1e,
++		0x1f,0xcd,0xc9,0x48,0xc7,0x7b,0xea,0xe9,0xa6,0xf0,0xe8,0x7c,0xe1,0xa8,0x05,0x1c,
++		0x8f,0x80,0x87,0x68,0x5c,0x12,0x62,0x4b,0xdf,0x58,0x38,0x0e,0xd6,0x6f,0x55,0xb4,
++		0x3d,0xdd,0x6d,0x36,0x21,0x73,0xa5,0xbd,0x38,0x98,0x59,0xc1,0x7d,0x95,0xec,0xe3,
++		0xab,0x73,0x26,0x39,0xff,0xe4,0x51,0xcd,0x10,0x3e,0xe4,0x85,0x4d,0xb2,0xf3,0x96, /* 3920 */
++		0x14,0xf6,0x58,0xba,0xa3,0x84,0xbc,0x99,0x48,0xd0,0x71,0x4e,0xb4,0x8a,0x88,0x71,
++		0x43,0xe7,0xa1,0xfa,0x4b,0x69,0x0c,0x22,0xb4,0x92,0xa7,0x0c,0x61,0x2b,0x59,0xff,
++		0xd2,0xd6,0xb3,0xb5,0xe9,0x9c,0x20,0x03,0xe2,0xc3,0x59,0xb1,0xe6,0x2d,0xcb,0x62,
++		0x0c,0x7a,0x24,0x6a,0x7b,0x9b,0x32,0x46,0x13,0x15,0x56,0xf2,0xf3,0xd5,0x13,0xa2,
++		0x3c,0x6a,0x9f,0xd2,0x28,0x0e,0xd6,0x86,0xd7,0x67,0xcc,0xd0,0x17,0x54,0xeb,0x4c, /* 4000 */
++		0x99,0x69,0x2f,0x2b,0x38,0x0c,0x36,0x08,0x13,0x44,0xc1,0xd3,0x5e,0xe1,0x94,0x97,
++		0x36,0xb6,0x97,0x6f,0x48,0x52,0xcf,0xbe,0x64,0xfa,0xbc,0xf1,0x1b,0x9a,0xfb,0x82,
++		0x85,0x76,0xb4,0xf9,0x78,0x7a,0xa7,0xd0,0x3e,0x84,0x59,0x8a,0x71,0x43,0xef,0x73,
++		0x11,0xfa,0xf2,0x97,0x0e,0x23,0xed,0x4c,0x17,0x3f,0x98,0x5d,0x64,0x50,0x16,0x5a,
++		0xe3,0xe2,0x41,0xa1,0x82,0x34,0xe7,0x4f,0xf3,0xdd,0xb9,0x21,0xa5,0x30,0x0b,0x1c, /* 4080 */
++		0x4f,0xb6,0xe4,0x32,0xe6,0x98,0xf5,0x3f,0x66,0xe3,0x8c,0x07,0xbc,0xd6,0xe7,0x76,
++		0x05,0xdf,0x46,0x24,0xd5,0x79,0x07,0x62,0x92,0xde,0x1c,0xe6,0xfc,0x6f,0x00,0x81,
++		0xa3,0x8b,0xd9,0x2d,0x39,0xb2,0x4b,0x73,0xba,0xc1,0xc5,0x2b,0xd6,0x8e,0x91,0x81,
++		0xd3,0xdc,0xd0,0xac,0x75,0x34,0xdb,0x48,0x90,0x1e,0x59,0x84,0xf9,0x90,0x25,0x57,
++		0xbf,0xa2,0x31,0xb2,0xea,0x28,0xc3,0x18,0x32,0x62,0xa1,0xb2,0x22,0x1f,0x74,0x26, /* 4160 */
++		0xea,0x88,0xa5,0x81,0x60,0x93,0xa5,0xca,0xe2,0xcd,0x5d,0x59,0xa9,0x39,0x0f,0xc9,
++		0x3a,0x29,0x56,0x94,0x4b,0x06,0x4c,0xf0,0x13,0xbc,0xdb,0x67,0xfb,0x42,0x3d,0x13,
++		0x28,0xd2,0xc6,0xd7,0xba,0x32,0x90,0x13,0xfa,0x2d,0x30,0xef,0xd6,0x9f,0xdc,0xa1,
++		0xa9,0x5e,0xa6,0xd0,0x6c,0x73,0x63,0x53,0x4b,0x2f,0x3f,0x7d,0xaa,0xfa,0x29,0x6e,
++		0xaa,0x09,0xb3,0x66,0x8e,0x9c,0xf8,0x2d,0x9b,0xa9,0x59,0xb3,0x2f,0x3c,0xad,0x3c, /* 4240 */
++		0x10,0xc6,0xea,0x48,0x61,0x15,0x54,0x53,0x9c,0x37,0xdf,0x6b,0xca,0x33,0x85,0xea,
++		0xd3,0xfc,0xff,0x96,0xd3,0x72,0xb4,0x23,0x93,0xb7,0x3c,0x8d,0xaa,0xaa,0x31,0x50,
++		0x6e,0xe0,0x52,0x7b,0x7f,0xb3,0xe5,0x93,0xdc,0xcc,0xa5,0x7c,0x8f,0xbb,0xd4,0xa3,
++		0xc7,0xf8,0xa5,0x38,0x99,0x86,0x91,0x32,0xfb,0xc3,0xe4,0x05,0x06,0x07,0xbb,0xfe,
++		0x29,0xc6,0x75,0xe3,0x94,0x5e,0x74,0xa3,0x1c,0xd5,0x31,0xba,0x7a,0xeb,0x2e,0x2f, /* 4320 */
++		0x0c,0xd9,0x90,0xb8,0xf9,0x83,0xa9,0x0d,0xfe,0xa0,0x56,0x8f,0x06,0x77,0xea,0x95,
++		0x63,0xf7,0xc4,0x79,0xde,0x96,0x89,0x40,0xcf,0x24,0x29,0x92,0x69,0x28,0x65,0xcf,
++		0xda,0x89,0xfa,0x07,0x8b,0xbe,0xf4,0x9c,0xe4,0x57,0x5b,0xdf,0xb3,0x80,0x36,0x60,
++		0x11,0xc8,0x43,0x5f,0x12,0xb4,0x2d,0x9a,0xb9,0x9a,0xb6,0xa3,0x19,0x12,0xc4,0x35,
++		0x41,0x49,0xd7,0x23,0x10,0x1d,0x13,0x65,0xa6,0x5e,0x7c,0xc6,0x8d,0x82,0xe3,0x05, /* 4400 */
++		0x17,0x77,0x39,0x02,0xfb,0x38,0xdd,0xa2,0xb3,0x24,0xe7,0x20,0x8e,0x98,0x7e,0xd2,
++		0x87,0xd0,0x92,0xe7,0x66,0x2a,0x43,0x02,0x41,0xbf,0xca,0x55,0x2d,0x31,0x41,0x27,
++		0xe3,0x8c,0x85,0x97,0xa8,0x95,0x19,0xd4,0xf1,0xe6,0x2a,0x79,0x46,0x5a,0xd5,0xf4,
++		0xea,0xa3,0xfa,0x77,0xcd,0x98,0x32,0x6d,0x2f,0x92,0xce,0x98,0x52,0x05,0x5c,0xec,
++		0xcf,0x62,0xd6,0x3c,0xb9,0xd7,0xf1,0x98,0xae,0x08,0x5e,0x4d,0x45,0xc8,0xe4,0x8f, /* 4480 */
++		0xcf,0xfe,0x59,0x3a,0xd6,0x52,0xd9,0x15,0x41,0x67,0xbf,0x3e,0x61,0x95,0x81,0x0a,
++		0x44,0x5a,0xe1,0x58,0xf1,0xf9,0xa6,0x79,0x33,0x63,0xaf,0xc1,0xf2,0x2c,0xa8,0x82,
++		0xfe,0xed,0x3a,0x5f,0x57,0x27,0xca,0x76,0x47,0x7c,0x5f,0x23,0xf0,0xfc,0x87,0x00,
++		0xcd,0xc6,0xa5,0xbc,0xb2,0xb2,0x0b,0x4f,0x92,0x66,0x35,0x1d,0x30,0x4a,0x96,0xa8,
++		0x2b,0xf5,0xf3,0x14,0xaf,0x68,0x5c,0x1c,0x70,0x7c,0x92,0xe3,0xe8,0x47,0xb7,0x04, /* 4560 */
++		0x7d,0x68,0x9c,0x70,0xb2,0x5e,0x55,0x01,0xca,0xec,0x99,0x19,0x62,0x6f,0x4a,0x0f,
++		0xc8,0x15,0x86,0xaf,0x1e,0xc8,0x88,0x89,0xb4,0x23,0x38,0x7d,0x5d,0x95,0x48,0x26,
++		0x18,0xa6,0x50,0xe8,0x0b,0x53,0xb0,0x7c,0xac,0xe3,0x22,0x89,0x40,0x60,0x2e,0x3d,
++		0xb4,0x74,0x66,0xce,0x9b,0xcc,0xb6,0xe4,0xd8,0xaa,0x61,0xc8,0x91,0x25,0x83,0xe8,
++		0x10,0xb3,0xb2,0xe7,0xe9,0xcb,0x48,0xbd,0x40,0x3e,0xcf,0x08,0xd2,0x8c,0x70,0xae, /* 4640 */
++		0x0b,0x62,0x08,0x59,0xc1,0xf0,0x9b,0x61,0x13,0x14,0x04,0xc3,0xd5,0xbf,0xfc,0xd8,
++		0x60,0xe0,0xf4,0x2a,0xb2,0x99,0x00,0x62,0x30,0xb2,0x87,0x6d,0x77,0xdd,0xa9,0x1c,
++		0x8c,0x62,0xbd,0x93,0xa8,0x44,0xe4,0xb3,0x44,0xe3,0x25,0x5e,0xea,0x53,0x1c,0x6c,
++		0x45,0x8d,0x04,0xab,0xdb,0x0f,0xae,0xf2,0xd1,0xc0,0xb4,0xc5,0x5f,0x57,0x0a,0x5a,
++		0x51,0x02,0x3f,0x4d,0x4e,0xff,0xf5,0x9f,0x9a,0xbe,0x17,0x92,0x2f,0xe7,0x32,0xca, /* 4720 */
++		0x71,0xbc,0xd4,0x34,0xad,0x77,0x10,0xb8,0x4c,0xd4,0xac,0x9f,0x25,0x07,0xa0,0x68,
++		0x26,0x56,0x2a,0xd7,0xf6,0x47,0x82,0x6f,0x9d,0xbb,0xe4,0xed,0xd2,0x3f,0x12,0x43,
++		0x69,0xdb,0x85,0x26,0xfc,0x2b,0x4d,0x52,0xf0,0x74,0x14,0x15,0xf9,0x72,0xbe,0xf6,
++		0xa9,0x35,0xbd,0x81,0x2a,0x56,0xc8,0x22,0x1b,0x7d,0xef,0x0f,0x51,0x06,0xbc,0x01,
++		0xe9,0x13,0xe3,0xd4,0x3d,0xb8,0x6c,0x2b,0xb4,0xc7,0xe0,0x76,0x26,0x63,0xc6,0xde, /* 4800 */
++		0x78,0x87,0x21,0xc2,0xaa,0x07,0xf8,0x95,0x48,0x87,0xe2,0x14,0x2f,0x2e,0x91,0x4a,
++		0x09,0x9e,0xfc,0x0a,0xee,0x13,0x39,0x21,0x0d,0x3e,0x53,0xda,0x3e,0xcf,0x88,0x62,
++		0x4b,0x11,0x19,0xbe,0x34,0x01,0x0b,0x88,0x6c,0x80,0xf5,0x1d,0x18,0x50,0x83,0x8f,
++		0x21,0x50,0xe7,0x2b,0x04,0x2a,0xf3,0x28,0x99,0xc0,0xd3,0xd7,0xb0,0x2a,0x57,0xf8,
++		0xcf,0x26,0x3a,0x36,0x95,0x62,0xe4,0xe9,0x45,0xa3,0x12,0x82,0xa5,0x02,0xa9,0x5e, /* 4880 */
++		0xe9,0xbb,0x03,0x16,0xc6,0x86,0x10,0x06,0xda,0xc1,0x7f,0x93,0x6f,0x54,0xc4,0xc7),/* 4896 */
+ 	  chunk_from_chars(
+ 		0x30,0x82,0x0a,0x32,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x03,
+ 		0x13,0x03,0x82,0x0a,0x21,0x00),
+@@ -1849,9 +2591,9 @@ START_TEST(test_ml_dsa_sig)
+ 	private_key_t *key;
+ 	signature_scheme_t scheme;
+ 	pqc_params_t pqc_params;
+-	chunk_t msg, signature;
++	chunk_t msg, signature, asn1, asn1_privkey;
+ 
+-	/* load private key */
++	/* load private key in seed format*/
+ 	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, tests[_i].type,
+ 							 BUILD_BLOB, tests[_i].seed, BUILD_END);
+ 	ck_assert(key != NULL);
+@@ -1884,10 +2626,49 @@ START_TEST(test_ml_dsa_sig)
+ 	/* generate and verify signature with empty context string */
+ 	ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
+ 	ck_assert_chunk_eq(signature, tests[_i].sig);
+-
+-	/* cleanup */
+ 	chunk_free(&signature);
+ 	key->destroy(key);
++
++	/* load private key in expanded format*/
++	asn1 = chunk_cat("cc", tests[_i].asn1_privkey, tests[_i].privkey);
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
++							 BUILD_BLOB_ASN1_DER, asn1, BUILD_END);
++	chunk_free(&asn1);
++
++	if (key == NULL)
++	{
++		warn("ML-DSA private key in expanded format not supported");
++	}
++	else
++	{
++		/* generate and verify signature with empty context string */
++		ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
++		ck_assert_chunk_eq(signature, tests[_i].sig);
++		chunk_free(&signature);
++		key->destroy(key);
++	}
++
++	/* load private key in combined seed/expanded format*/
++	asn1_privkey = chunk_skip(tests[_i].asn1_privkey,
++							  tests[_i].asn1_privkey.len - 4);
++	asn1 = chunk_cat("cccc", tests[_i].asn1_seed_key, tests[_i].seed,
++							 asn1_privkey, tests[_i].privkey);
++	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
++							 BUILD_BLOB_ASN1_DER, asn1, BUILD_END);
++	chunk_free(&asn1);
++
++	if (key == NULL)
++	{
++		warn("ML-DSA private key in both format not supported");
++	}
++	else
++	{
++		/* generate and verify signature with empty context string */
++		ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
++		ck_assert_chunk_eq(signature, tests[_i].sig);
++		chunk_free(&signature);
++		key->destroy(key);
++	}
+ }
+ END_TEST
+ 
+
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+index 359ce4ec326..4113b1c4b73 100644
+--- a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
++++ b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+@@ -976,7 +976,8 @@ private_key_t *ml_dsa_private_key_gen(key_type_t type, va_list args)
+ private_key_t *ml_dsa_private_key_load(key_type_t type, va_list args)
+ {
+ 	private_private_key_t *this;
+-	chunk_t priv = chunk_empty;
++	chunk_t priv = chunk_empty, seed = chunk_empty;
++	int asn1_type;
+ 
+ 	while (TRUE)
+ 	{
+@@ -997,17 +998,6 @@ private_key_t *ml_dsa_private_key_load(key_type_t type, va_list args)
+ 	{
+ 		return NULL;
+ 	}
+-	if (priv.len == ML_DSA_SEED_LEN + 2 &&
+-		priv.ptr[0] == 0x80 && priv.ptr[1] == ML_DSA_SEED_LEN)
+-	{
+-		priv = chunk_skip(priv, 2);
+-	}
+-	if (priv.len != ML_DSA_SEED_LEN)
+-	{
+-		DBG1(DBG_LIB, "error: the size of the loaded ML-DSA private key seed is "
+-					  "%u bytes instead of %d bytes", priv.len, ML_DSA_SEED_LEN);
+-		return NULL;
+-	}
+ 
+ 	this = create_instance(type);
+ 	if (!this)
+@@ -1015,10 +1005,78 @@ private_key_t *ml_dsa_private_key_load(key_type_t type, va_list args)
+ 		return NULL;
+ 	}
+ 
+-	if (!generate_keypair(this, chunk_clone(priv)))
++	if (priv.len == ML_DSA_SEED_LEN)
+ 	{
+-		destroy(this);
+-		return NULL;
++		asn1_type = ASN1_CONTEXT_S_0;
++	}
++	else
++	{
++		asn1_type = asn1_unwrap(&priv, &priv);
++	}
++
++	/* three supported ML-DSA private key formats */
++	switch(asn1_type)
++	{
++		/* private key in seed-only format */
++		case ASN1_CONTEXT_S_0:
++			seed = priv;
++			if (seed.len != ML_DSA_SEED_LEN ||
++			   !generate_keypair(this, chunk_clone(seed)))
++			{
++				DBG1(DBG_LIB, "failed to load ML-DSA private key seed");
++				destroy(this);
++				return NULL;
++			}
++			break;
++
++		/* private key in epanded format */
++		case ASN1_OCTET_STRING:
++			if (priv.len != this->params->privkey_len)
++			{
++				DBG1(DBG_LIB, "failed to load ML-DSA expanded private key");
++				destroy(this);
++				return NULL;
++			}
++			memcpy(this->privkey.ptr, priv.ptr, priv.len);
++			break;
++
++		/* private key in both seed and expanded format */
++		case ASN1_SEQUENCE:
++			if (asn1_unwrap(&priv, &seed) != ASN1_OCTET_STRING ||
++				seed.len != ML_DSA_SEED_LEN)
++			{
++				DBG1(DBG_LIB, "failed to identify ML-DSA private key seed");
++				destroy(this);
++				return NULL;
++			}
++			if (!generate_keypair(this, chunk_clone(seed)))
++			{
++				DBG1(DBG_LIB, "failed to load ML-DSA private key seed");
++				destroy(this);
++				return NULL;
++			}
++			if (asn1_unwrap(&priv, &priv) != ASN1_OCTET_STRING ||
++				priv.len != this->params->privkey_len)
++			{
++				DBG1(DBG_LIB, "failed to identify ML-DSA expanded private key");
++				destroy(this);
++				return NULL;
++			}
++			if (!chunk_equals(priv, this->privkey))
++			{
++				DBG1(DBG_LIB, "loaded expanded private key is not derived "
++							  "from loaded seed");
++				destroy(this);
++				return NULL;
++			}
++			break;
++
++		/* invalid private key format */
++		case ASN1_INVALID:
++		default:
++			DBG1(DBG_LIB, "unknown ML-DSA private key format");
++			destroy(this);
++			return NULL;
+ 	}
+ 
+ 	return &this->public;
+
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+index 4113b1c4b73..06280b60161 100644
+--- a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
++++ b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+@@ -516,14 +516,23 @@ METHOD(private_key_t, get_encoding, bool,
+ 		{
+ 			bool success = TRUE;
+ 			int oid = key_type_to_oid(this->type);
++			int asn1_type = ASN1_CONTEXT_S_0;
++			chunk_t content = this->keyseed;
++
++			/* generally, we produce a seed-only encoding, however, if we loaded
++			 * an expanded key without seed, we can't, so export it as such */
++			if (!content.len)
++			{
++				asn1_type = ASN1_OCTET_STRING;
++				content = this->privkey;
++			}
+ 
+ 			*encoding = asn1_wrap(ASN1_SEQUENCE, "cmm",
+ 							ASN1_INTEGER_0,
+ 							asn1_algorithmIdentifier(oid),
+ 							asn1_wrap(ASN1_OCTET_STRING, "m",
+-								asn1_simple_object(ASN1_CONTEXT_S_0,
+-												   this->keyseed))
+-						);
++								asn1_simple_object(asn1_type, content)));
++
+ 			if (type == PRIVKEY_PEM)
+ 			{
+ 				chunk_t asn1_encoding = *encoding;
+diff --git a/src/libstrongswan/tests/suites/test_ml_dsa.c b/src/libstrongswan/tests/suites/test_ml_dsa.c
+index 878bc531149..3af6030ce9b 100644
+--- a/src/libstrongswan/tests/suites/test_ml_dsa.c
++++ b/src/libstrongswan/tests/suites/test_ml_dsa.c
+@@ -2591,9 +2591,9 @@ START_TEST(test_ml_dsa_sig)
+ 	private_key_t *key;
+ 	signature_scheme_t scheme;
+ 	pqc_params_t pqc_params;
+-	chunk_t msg, signature, asn1, asn1_privkey;
++	chunk_t msg, signature, asn1, asn1_privkey, encoding;
+ 
+-	/* load private key in seed format*/
++	/* load private key in seed format */
+ 	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, tests[_i].type,
+ 							 BUILD_BLOB, tests[_i].seed, BUILD_END);
+ 	ck_assert(key != NULL);
+@@ -2629,11 +2629,10 @@ START_TEST(test_ml_dsa_sig)
+ 	chunk_free(&signature);
+ 	key->destroy(key);
+ 
+-	/* load private key in expanded format*/
++	/* load private key in expanded format */
+ 	asn1 = chunk_cat("cc", tests[_i].asn1_privkey, tests[_i].privkey);
+ 	key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
+ 							 BUILD_BLOB_ASN1_DER, asn1, BUILD_END);
+-	chunk_free(&asn1);
+ 
+ 	if (key == NULL)
+ 	{
+@@ -2641,14 +2640,19 @@ START_TEST(test_ml_dsa_sig)
+ 	}
+ 	else
+ 	{
++		/* check ML-DSA private key encoding (expanded-only) */
++		ck_assert(key->get_encoding(key, PRIVKEY_ASN1_DER, &encoding));
++		ck_assert_chunk_eq(encoding, asn1);
++		chunk_free(&encoding);
+ 		/* generate and verify signature with empty context string */
+ 		ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
+ 		ck_assert_chunk_eq(signature, tests[_i].sig);
+ 		chunk_free(&signature);
+ 		key->destroy(key);
+ 	}
++	chunk_free(&asn1);
+ 
+-	/* load private key in combined seed/expanded format*/
++	/* load private key in combined seed/expanded format */
+ 	asn1_privkey = chunk_skip(tests[_i].asn1_privkey,
+ 							  tests[_i].asn1_privkey.len - 4);
+ 	asn1 = chunk_cat("cccc", tests[_i].asn1_seed_key, tests[_i].seed,
+@@ -2663,6 +2667,12 @@ START_TEST(test_ml_dsa_sig)
+ 	}
+ 	else
+ 	{
++		/* check ML-DSA private key encoding (seed-only) */
++		asn1 = chunk_cat("cc", tests[_i].asn1_seed, tests[_i].seed);
++		ck_assert(key->get_encoding(key, PRIVKEY_ASN1_DER, &encoding));
++		ck_assert_chunk_eq(encoding, asn1);
++		chunk_free(&asn1);
++		chunk_free(&encoding);
+ 		/* generate and verify signature with empty context string */
+ 		ck_assert(key->sign(key, scheme, &pqc_params, msg, &signature));
+ 		ck_assert_chunk_eq(signature, tests[_i].sig);
+
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c
+index 7c21902a722..2d57d8429fb 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c
++++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.c
+@@ -42,10 +42,11 @@ typedef struct private_openssl_ec_public_key_t private_openssl_ec_public_key_t;
+  * Private data structure with signing context.
+  */
+ struct private_openssl_ec_public_key_t {
++
+ 	/**
+-	 * Public interface for this signer.
++	 * Public interface
+ 	 */
+-	openssl_ec_public_key_t public;
++	public_key_t public;
+ 
+ 	/**
+ 	 * EC key object
+@@ -282,7 +283,7 @@ METHOD(public_key_t, get_ref, public_key_t*,
+ 	private_openssl_ec_public_key_t *this)
+ {
+ 	ref_get(&this->ref);
+-	return &this->public.key;
++	return &this->public;
+ }
+ 
+ METHOD(public_key_t, destroy, void,
+@@ -319,13 +320,44 @@ bool openssl_check_explicit_params(const EVP_PKEY *key)
+ 	return explicit == 1;
+ }
+ 
++/*
++ * Described in header
++ */
++public_key_t *openssl_ec_public_key_create(EVP_PKEY *key)
++{
++	private_openssl_ec_public_key_t *this;
++
++	if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC ||
++		openssl_check_explicit_params(key))
++	{
++		EVP_PKEY_free(key);
++		return NULL;
++	}
++
++	INIT(this,
++		.public = {
++			.get_type = _get_type,
++			.verify = _verify,
++			.encrypt = _encrypt,
++			.get_keysize = _get_keysize,
++			.equals = public_key_equals,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = public_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
++		},
++		.ref = 1,
++		.key = key,
++	);
++	return &this->public;
++}
++
+ /**
+  * See header.
+  */
+-openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
+-													va_list args)
++public_key_t *openssl_ec_public_key_load(key_type_t type, va_list args)
+ {
+-	private_openssl_ec_public_key_t *this;
+ 	chunk_t blob = chunk_empty;
+ 	EVP_PKEY *key;
+ 
+@@ -344,32 +376,7 @@ openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
+ 		break;
+ 	}
+ 	key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
+-	if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC ||
+-		openssl_check_explicit_params(key))
+-	{
+-		EVP_PKEY_free(key);
+-		return NULL;
+-	}
+-
+-	INIT(this,
+-		.public = {
+-			.key = {
+-				.get_type = _get_type,
+-				.verify = _verify,
+-				.encrypt = _encrypt,
+-				.get_keysize = _get_keysize,
+-				.equals = public_key_equals,
+-				.get_fingerprint = _get_fingerprint,
+-				.has_fingerprint = public_key_has_fingerprint,
+-				.get_encoding = _get_encoding,
+-				.get_ref = _get_ref,
+-				.destroy = _destroy,
+-			},
+-		},
+-		.ref = 1,
+-		.key = key,
+-	);
+-	return &this->public;
++	return openssl_ec_public_key_create(key);
+ }
+ 
+ #endif /* OPENSSL_NO_ECDSA */
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h
+index 7b380487de5..19a3092a810 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h
++++ b/src/libstrongswan/plugins/openssl/openssl_ec_public_key.h
+@@ -15,6 +15,8 @@
+  */
+ 
+ /**
++ * public_key_t implementation of ECDSA using OpenSSL
++ *
+  * @defgroup openssl_ec_public_key openssl_ec_public_key
+  * @{ @ingroup openssl_p
+  */
+@@ -22,22 +24,10 @@
+ #ifndef OPENSSL_EC_PUBLIC_KEY_H_
+ #define OPENSSL_EC_PUBLIC_KEY_H_
+ 
+-typedef struct openssl_ec_public_key_t openssl_ec_public_key_t;
++#include <openssl/evp.h>
+ 
+-#include <credentials/builder.h>
+ #include <credentials/keys/public_key.h>
+ 
+-/**
+- * public_key_t implementation of ECDSA using OpenSSL.
+- */
+-struct openssl_ec_public_key_t {
+-
+-	/**
+-	 * Implements the public_key_t interface
+-	 */
+-	public_key_t key;
+-};
+-
+ /**
+  * Load a ECDSA public key using OpenSSL.
+  *
+@@ -47,7 +37,14 @@ struct openssl_ec_public_key_t {
+  * @param args		builder_part_t argument list
+  * @return 			loaded key, NULL on failure
+  */
+-openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type,
+-													va_list args);
++public_key_t *openssl_ec_public_key_load(key_type_t type, va_list args);
++
++/**
++ * Wrap an EVP_PKEY object of type ECDSA.
++ *
++ * @param key		EVP_PKEY object (adopted)
++ * @return 			generated key, NULL on failure
++ */
++public_key_t *openssl_ec_public_key_create(EVP_PKEY *key);
+ 
+ #endif /** OPENSSL_EC_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c
+index e676fe8977c..ecaca1b3a01 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c
++++ b/src/libstrongswan/plugins/openssl/openssl_ed_private_key.c
+@@ -67,6 +67,7 @@ int asn1_unwrap(chunk_t*, chunk_t*);
+ 
+ /* from ed public key */
+ int openssl_ed_key_type(key_type_t type);
++key_type_t openssl_ed_evp_pkey_key_type(EVP_PKEY *key);
+ int openssl_ed_keysize(key_type_t type);
+ bool openssl_ed_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp);
+ 
+@@ -258,17 +259,11 @@ private_key_t *openssl_ed_private_key_create(EVP_PKEY *key, bool engine)
+ 	private_private_key_t *this;
+ 	key_type_t type;
+ 
+-	switch (EVP_PKEY_base_id(key))
++	type = openssl_ed_evp_pkey_key_type(key);
++	if (type == KEY_ANY)
+ 	{
+-		case EVP_PKEY_ED25519:
+-			type = KEY_ED25519;
+-			break;
+-		case EVP_PKEY_ED448:
+-			type = KEY_ED448;
+-			break;
+-		default:
+-			EVP_PKEY_free(key);
+-			return NULL;
++		EVP_PKEY_free(key);
++		return NULL;
+ 	}
+ 
+ 	this = create_internal(type, key);
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c
+index d6ea2be3123..79bb2c33d5d 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c
++++ b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.c
+@@ -69,6 +69,22 @@ int openssl_ed_key_type(key_type_t type)
+ 	}
+ }
+ 
++/**
++ * Map an EVP_PKEY type to a key type
++ */
++key_type_t openssl_ed_evp_pkey_key_type(EVP_PKEY *key)
++{
++	switch (EVP_PKEY_base_id(key))
++	{
++		case EVP_PKEY_ED25519:
++			return KEY_ED25519;
++		case EVP_PKEY_ED448:
++			return KEY_ED448;
++		default:
++			return KEY_ANY;
++	}
++}
++
+ /**
+  * Map a key type to a key size
+  */
+@@ -249,6 +265,28 @@ static private_public_key_t *create_empty(key_type_t type)
+ 	return this;
+ }
+ 
++/*
++ * Described in header
++ */
++public_key_t *openssl_ed_public_key_create(EVP_PKEY *key)
++{
++	private_public_key_t *this;
++	key_type_t type;
++
++	if (key)
++	{
++		type = openssl_ed_evp_pkey_key_type(key);
++		if (type != KEY_ANY)
++		{
++			this = create_empty(type);
++			this->key = key;
++			return &this->public;
++		}
++		EVP_PKEY_free(key);
++	}
++	return NULL;
++}
++
+ /*
+  * Described in header
+  */
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h
+index 29155d1686c..0e3b34b55e8 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h
++++ b/src/libstrongswan/plugins/openssl/openssl_ed_public_key.h
+@@ -36,4 +36,12 @@
+  */
+ public_key_t *openssl_ed_public_key_load(key_type_t type, va_list args);
+ 
++/**
++ * Wrap an EVP_PKEY object of type EdDSA.
++ *
++ * @param key		EVP_PKEY object (adopted)
++ * @return 			generated key, NULL on failure
++ */
++public_key_t *openssl_ed_public_key_create(EVP_PKEY *key);
++
+ #endif /** OPENSSL_ED_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
+index c1263da3c4a..2d739affef8 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
++++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.c
+@@ -283,7 +283,7 @@ METHOD(public_key_t, destroy, void,
+ /**
+  * Generic private constructor
+  */
+-static private_public_key_t *create_empty(key_type_t type)
++static public_key_t *create_internal(key_type_t type, EVP_PKEY *key)
+ {
+ 	private_public_key_t *this;
+ 
+@@ -301,10 +301,27 @@ static private_public_key_t *create_empty(key_type_t type)
+ 			.destroy = _destroy,
+ 		},
+ 		.type = type,
++		.key = key,
+ 		.ref = 1,
+ 	);
+ 
+-	return this;
++	return &this->public;
++}
++
++/*
++ * Described in header
++ */
++public_key_t *openssl_ml_dsa_public_key_create(EVP_PKEY *key)
++{
++	key_type_t type;
++
++	type = openssl_ml_dsa_evp_pkey_key_type(key);
++	if (type == KEY_ANY)
++	{
++		EVP_PKEY_free(key);
++		return NULL;
++	}
++	return create_internal(type, key);
+ }
+ 
+ /*
+@@ -312,8 +329,7 @@ static private_public_key_t *create_empty(key_type_t type)
+  */
+ public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args)
+ {
+-	private_public_key_t *this;
+-	chunk_t pkcs1, blob = chunk_empty;
++	chunk_t pkcs1 = chunk_empty, blob = chunk_empty;
+ 	EVP_PKEY *key = NULL;
+ 
+ 	while (TRUE)
+@@ -338,25 +354,24 @@ public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args)
+ 	{
+ 		key = EVP_PKEY_new_raw_public_key(openssl_ml_dsa_key_type(type), NULL,
+ 										  blob.ptr, blob.len);
++		if (key)
++		{
++			return create_internal(type, key);
++		}
+ 	}
+ 	else if (pkcs1.len)
+ 	{
+ 		key = d2i_PUBKEY(NULL, (const u_char**)&pkcs1.ptr, pkcs1.len);
+-		type = openssl_ml_dsa_evp_pkey_key_type(key);
+-		if (key && type == KEY_ANY)
++		if (key)
+ 		{
++			if (type == openssl_ml_dsa_evp_pkey_key_type(key))
++			{
++				return create_internal(type, key);
++			}
+ 			EVP_PKEY_free(key);
+-			return NULL;
+ 		}
+ 	}
+-	if (!key)
+-	{
+-		return NULL;
+-	}
+-	this = create_empty(type);
+-	this->key = key;
+-
+-	return &this->public;
++	return NULL;
+ }
+ 
+ #endif /* OPENSSL_VERSION_NUMBER */
+diff --git a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
+index 223830c5af5..222ffe616fc 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
++++ b/src/libstrongswan/plugins/openssl/openssl_ml_dsa_public_key.h
+@@ -36,4 +36,12 @@
+  */
+ public_key_t *openssl_ml_dsa_public_key_load(key_type_t type, va_list args);
+ 
++/**
++ * Wrap an EVP_PKEY object of type ML-DSA-44/64/87.
++ *
++ * @param key		EVP_PKEY object (adopted)
++ * @return 			generated key, NULL on failure
++ */
++public_key_t *openssl_ml_dsa_public_key_create(EVP_PKEY *key);
++
+ #endif /** OPENSSL_ML_DSA_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_plugin.c b/src/libstrongswan/plugins/openssl/openssl_plugin.c
+index 1dea3b60188..ca2911d5e44 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_plugin.c
++++ b/src/libstrongswan/plugins/openssl/openssl_plugin.c
+@@ -24,6 +24,7 @@
+ 
+ #include <openssl/err.h>
+ #include <openssl/evp.h>
++#include <openssl/x509.h>
+ #include <openssl/conf.h>
+ #include <openssl/rand.h>
+ #include <openssl/crypto.h>
+@@ -269,7 +270,7 @@ static bool seed_rng()
+ #endif /* OPENSSL_VERSION_NUMBER */
+ 
+ /**
+- * Generic key loader
++ * Generic private key loader
+  */
+ static private_key_t *openssl_private_key_load(key_type_t type, va_list args)
+ {
+@@ -298,6 +299,36 @@ static private_key_t *openssl_private_key_load(key_type_t type, va_list args)
+ 	return NULL;
+ }
+ 
++/**
++ * Generic public key loader
++ */
++static public_key_t *openssl_public_key_load(key_type_t type, va_list args)
++{
++	chunk_t blob = chunk_empty;
++	EVP_PKEY *key;
++
++	while (TRUE)
++	{
++		switch (va_arg(args, builder_part_t))
++		{
++			case BUILD_BLOB_ASN1_DER:
++				blob = va_arg(args, chunk_t);
++				continue;
++			case BUILD_END:
++				break;
++			default:
++				return NULL;
++		}
++		break;
++	}
++	if (blob.ptr)
++	{
++		key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
++		return openssl_wrap_public_key(key);
++	}
++	return NULL;
++}
++
+ METHOD(plugin_t, get_name, char*,
+ 	private_openssl_plugin_t *this)
+ {
+@@ -547,8 +578,6 @@ METHOD(plugin_t, get_features, int,
+ 			PLUGIN_PROVIDE(PRIVKEY_GEN, KEY_RSA),
+ 		PLUGIN_REGISTER(PUBKEY, openssl_rsa_public_key_load, TRUE),
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
+-		PLUGIN_REGISTER(PUBKEY, openssl_rsa_public_key_load, TRUE),
+-			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
+ 		/* signature/encryption schemes */
+ 		PLUGIN_PROVIDE(PRIVKEY_SIGN, SIGN_RSA_EMSA_PKCS1_NULL),
+ 		PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_NULL),
+@@ -678,7 +707,6 @@ METHOD(plugin_t, get_features, int,
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_44),
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_65),
+ 			PLUGIN_PROVIDE(PUBKEY, KEY_ML_DSA_87),
+-			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
+ 		PLUGIN_REGISTER(PRIVKEY, openssl_ml_dsa_private_key_load, TRUE),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_44),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ML_DSA_65),
+@@ -696,6 +724,8 @@ METHOD(plugin_t, get_features, int,
+ #endif /* OPENSSL_NO_ML_DSA */
+ 
+ 		/* generic key loader */
++		PLUGIN_REGISTER(PUBKEY, openssl_public_key_load, TRUE),
++			PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
+ 		PLUGIN_REGISTER(PRIVKEY, openssl_private_key_load, TRUE),
+ 			PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
+ 		PLUGIN_REGISTER(PRIVKEY, openssl_private_key_connect, FALSE),
+diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c
+index d259b22512c..9e41c6dc957 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c
++++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.c
+@@ -47,10 +47,11 @@ typedef struct private_openssl_rsa_public_key_t private_openssl_rsa_public_key_t
+  * Private data structure with signing context.
+  */
+ struct private_openssl_rsa_public_key_t {
++
+ 	/**
+-	 * Public interface for this signer.
++	 * Public interface
+ 	 */
+-	openssl_rsa_public_key_t public;
++	public_key_t public;
+ 
+ 	/**
+ 	 * RSA key object
+@@ -479,7 +480,7 @@ METHOD(public_key_t, get_ref, public_key_t*,
+ 	private_openssl_rsa_public_key_t *this)
+ {
+ 	ref_get(&this->ref);
+-	return &this->public.key;
++	return &this->public;
+ }
+ 
+ METHOD(public_key_t, destroy, void,
+@@ -505,18 +506,16 @@ static private_openssl_rsa_public_key_t *create_internal(EVP_PKEY *key)
+ 
+ 	INIT(this,
+ 		.public = {
+-			.key = {
+-				.get_type = _get_type,
+-				.verify = _verify,
+-				.encrypt = _encrypt,
+-				.equals = public_key_equals,
+-				.get_keysize = _get_keysize,
+-				.get_fingerprint = _get_fingerprint,
+-				.has_fingerprint = public_key_has_fingerprint,
+-				.get_encoding = _get_encoding,
+-				.get_ref = _get_ref,
+-				.destroy = _destroy,
+-			},
++			.get_type = _get_type,
++			.verify = _verify,
++			.encrypt = _encrypt,
++			.equals = public_key_equals,
++			.get_keysize = _get_keysize,
++			.get_fingerprint = _get_fingerprint,
++			.has_fingerprint = public_key_has_fingerprint,
++			.get_encoding = _get_encoding,
++			.get_ref = _get_ref,
++			.destroy = _destroy,
+ 		},
+ 		.ref = 1,
+ 		.key = key,
+@@ -525,10 +524,26 @@ static private_openssl_rsa_public_key_t *create_internal(EVP_PKEY *key)
+ 	return this;
+ }
+ 
++/*
++ * Described in header
++ */
++public_key_t *openssl_rsa_public_key_create(EVP_PKEY *key)
++{
++	private_openssl_rsa_public_key_t *this;
++
++	if (EVP_PKEY_base_id(key) != EVP_PKEY_RSA)
++	{
++		EVP_PKEY_free(key);
++		return NULL;
++	}
++	this = create_internal(key);
++	return &this->public;
++}
++
+ /**
+  * See header.
+  */
+-openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
++public_key_t *openssl_rsa_public_key_load(key_type_t type,
+ 													  va_list args)
+ {
+ 	private_openssl_rsa_public_key_t *this;
+@@ -559,40 +574,23 @@ openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
+ 
+ 	if (blob.ptr)
+ 	{
+-		switch (type)
+-		{
+-			case KEY_ANY:
+-				key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len);
+-				if (key && EVP_PKEY_base_id(key) != EVP_PKEY_RSA)
+-				{
+-					EVP_PKEY_free(key);
+-					key = NULL;
+-				}
+-				break;
+-			case KEY_RSA:
+ #if defined(OPENSSL_IS_BORINGSSL) && \
+ 	(!defined(BORINGSSL_API_VERSION) || BORINGSSL_API_VERSION < 10)
+-			{
+-				RSA *rsa = d2i_RSAPublicKey(NULL, (const u_char**)&blob.ptr,
+-											blob.len);
+-				key = EVP_PKEY_new();
+-				if (!key || !EVP_PKEY_assign_RSA(key, rsa))
+-				{
+-					RSA_free(rsa);
+-					EVP_PKEY_free(key);
+-					key = NULL;
+-				}
+-			}
+-#else
+-				key = d2i_PublicKey(EVP_PKEY_RSA, NULL, (const u_char**)&blob.ptr,
++		RSA *rsa = d2i_RSAPublicKey(NULL, (const u_char**)&blob.ptr,
+ 									blob.len);
+-#endif
+-				break;
+-			default:
+-				break;
++		key = EVP_PKEY_new();
++		if (!key || !EVP_PKEY_assign_RSA(key, rsa))
++		{
++			RSA_free(rsa);
++			EVP_PKEY_free(key);
++			key = NULL;
+ 		}
++#else
++		key = d2i_PublicKey(EVP_PKEY_RSA, NULL, (const u_char**)&blob.ptr,
++							blob.len);
++#endif
+ 	}
+-	else if (n.ptr && e.ptr && type == KEY_RSA)
++	else if (n.ptr && e.ptr)
+ 	{
+ 		BIGNUM *bn_n, *bn_e;
+ 
+diff --git a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h
+index 77a573a638a..a3945476b3a 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h
++++ b/src/libstrongswan/plugins/openssl/openssl_rsa_public_key.h
+@@ -15,6 +15,8 @@
+  */
+ 
+ /**
++ * public_key_t implementation of RSA algorithm using OpenSSL.
++ *
+  * @defgroup openssl_rsa_public_key openssl_rsa_public_key
+  * @{ @ingroup openssl_p
+  */
+@@ -22,21 +24,10 @@
+ #ifndef OPENSSL_RSA_PUBLIC_KEY_H_
+ #define OPENSSL_RSA_PUBLIC_KEY_H_
+ 
+-typedef struct openssl_rsa_public_key_t openssl_rsa_public_key_t;
++#include <openssl/evp.h>
+ 
+ #include <credentials/keys/public_key.h>
+ 
+-/**
+- * public_key_t implementation of RSA algorithm using OpenSSL.
+- */
+-struct openssl_rsa_public_key_t {
+-
+-	/**
+-	 * Implements the public_key_t interface
+-	 */
+-	public_key_t key;
+-};
+-
+ /**
+  * Load a RSA public key using OpenSSL.
+  *
+@@ -46,7 +37,14 @@ struct openssl_rsa_public_key_t {
+  * @param args		builder_part_t argument list
+  * @return 			loaded key, NULL on failure
+  */
+-openssl_rsa_public_key_t *openssl_rsa_public_key_load(key_type_t type,
+-													  va_list args);
++public_key_t *openssl_rsa_public_key_load(key_type_t type, va_list args);
++
++/**
++ * Wrap an EVP_PKEY object of type RSA.
++ *
++ * @param key		EVP_PKEY object (adopted)
++ * @return 			generated key, NULL on failure
++ */
++public_key_t *openssl_rsa_public_key_create(EVP_PKEY *key);
+ 
+ #endif /** OPENSSL_RSA_PUBLIC_KEY_H_ @}*/
+diff --git a/src/libstrongswan/plugins/openssl/openssl_util.c b/src/libstrongswan/plugins/openssl/openssl_util.c
+index 10027b37feb..d29ec6fbc8b 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_util.c
++++ b/src/libstrongswan/plugins/openssl/openssl_util.c
+@@ -29,9 +29,13 @@
+ #endif
+ 
+ #include "openssl_ec_private_key.h"
++#include "openssl_ec_public_key.h"
+ #include "openssl_ed_private_key.h"
++#include "openssl_ed_public_key.h"
+ #include "openssl_rsa_private_key.h"
++#include "openssl_rsa_public_key.h"
+ #include "openssl_ml_dsa_private_key.h"
++#include "openssl_ml_dsa_public_key.h"
+ 
+ /* these were added with 1.1.0 when ASN1_OBJECT was made opaque */
+ #if OPENSSL_VERSION_NUMBER < 0x10100000L
+@@ -178,6 +182,48 @@ private_key_t *openssl_wrap_private_key(EVP_PKEY *key, bool engine)
+ 	return NULL;
+ }
+ 
++/*
++ * Described in header
++ */
++public_key_t *openssl_wrap_public_key(EVP_PKEY *key)
++{
++	if (key)
++	{
++		switch (EVP_PKEY_base_id(key))
++		{
++#ifndef OPENSSL_NO_RSA
++			case EVP_PKEY_RSA:
++				return openssl_rsa_public_key_create(key);
++#endif
++#ifndef OPENSSL_NO_ECDSA
++			case EVP_PKEY_EC:
++				return openssl_ec_public_key_create(key);
++#endif
++#if OPENSSL_VERSION_NUMBER >= 0x1010100fL && !defined(OPENSSL_NO_EC)
++			case EVP_PKEY_ED25519:
++#ifndef OPENSSL_IS_AWSLC
++			case EVP_PKEY_ED448:
++#endif
++				return openssl_ed_public_key_create(key);
++#endif /* OPENSSL_VERSION_NUMBER && !OPENSSL_NO_EC && !OPENSSL_IS_AWSLC */
++			default:
++#if (OPENSSL_VERSION_NUMBER >= 0x30500000L && !defined(OPENSSL_NO_ML_DSA))
++				/* OpenSSL does not provide an ID for ML-DSA keys, which is
++				 * apparently a legacy feature */
++				if (EVP_PKEY_is_a(key, "ML-DSA-44") ||
++					EVP_PKEY_is_a(key, "ML-DSA-65") ||
++					EVP_PKEY_is_a(key, "ML-DSA-87"))
++				{
++					return openssl_ml_dsa_public_key_create(key);
++				}
++#endif
++				EVP_PKEY_free(key);
++				break;
++		}
++	}
++	return NULL;
++}
++
+ /**
+  * Described in header.
+  */
+diff --git a/src/libstrongswan/plugins/openssl/openssl_util.h b/src/libstrongswan/plugins/openssl/openssl_util.h
+index 7c72db2cceb..9e1ccb2ecc3 100644
+--- a/src/libstrongswan/plugins/openssl/openssl_util.h
++++ b/src/libstrongswan/plugins/openssl/openssl_util.h
+@@ -66,6 +66,14 @@ bool openssl_fingerprint(EVP_PKEY *key, cred_encoding_type_t type, chunk_t *fp);
+  */
+ private_key_t *openssl_wrap_private_key(EVP_PKEY *key, bool engine);
+ 
++/**
++ * Wrap the given OpenSSL public key in a type-specific public key object.
++ *
++ * @param key		key object to wrap
++ * @returns			created object or NULL if key type is not supported
++ */
++public_key_t *openssl_wrap_public_key(EVP_PKEY *key);
++
+ /**
+  * Concatenates two bignums into a chunk, thereby enforcing the length of
+  * a single BIGNUM, if necessary, by prepending it with zeros.
+
+diff --git a/src/libstrongswan/credentials/keys/signature_params.c b/src/libstrongswan/credentials/keys/signature_params.c
+index 44b6daacc9f..4ada4ccf10d 100644
+--- a/src/libstrongswan/credentials/keys/signature_params.c
++++ b/src/libstrongswan/credentials/keys/signature_params.c
+@@ -422,9 +422,8 @@ bool pqc_params_create(pqc_params_t *p_in, pqc_params_t *p_out)
+ 	{
+ 		p_out->deterministic = FALSE;
+ 
+-		p_out->pre_ctx = chunk_alloc(2);
++		p_out->pre_ctx = chunk_alloc(1);
+ 		p_out->pre_ctx.ptr[0] = 0x00;
+-		p_out->pre_ctx.ptr[1] = 0x00;
+ 	}
+ 	else
+ 	{
+@@ -437,12 +436,11 @@ bool pqc_params_create(pqc_params_t *p_in, pqc_params_t *p_out)
+ 						  p_in->ctx.len, PQC_MAX_CTX_LEN);
+ 			return FALSE;
+ 		}
+-		p_out->pre_ctx = chunk_alloc(2 + p_in->ctx.len);
+-		p_out->pre_ctx.ptr[0] = 0x00;
+-		p_out->pre_ctx.ptr[1] = (uint8_t)p_in->ctx.len;
+-		memcpy(p_out->pre_ctx.ptr + 2, p_in->ctx.ptr, p_in->ctx.len);
++		p_out->pre_ctx = chunk_alloc(p_in->ctx.len + 1);
++		p_out->pre_ctx.ptr[0] = (uint8_t)p_in->ctx.len;
++		memcpy(p_out->pre_ctx.ptr + 1, p_in->ctx.ptr, p_in->ctx.len);
+ 	}
+-	p_out->ctx = chunk_skip(p_out->pre_ctx, 2);
++	p_out->ctx = chunk_skip(p_out->pre_ctx, 1);
+ 
+ 	return TRUE;
+ }
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+index 06280b60161..6312380f7e8 100644
+--- a/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
++++ b/src/libstrongswan/plugins/ml/ml_dsa_private_key.c
+@@ -275,7 +275,7 @@ METHOD(private_key_t, sign, bool,
+ 	private_private_key_t *this, signature_scheme_t scheme,
+ 	void *params, chunk_t data, chunk_t *signature)
+ {
+-
++	const u_char pre_hash = 0;
+ 	const u_int k = this->params->k;
+ 	const u_int l = this->params->l;
+ 	const u_int gamma2 = this->params->gamma2;
+@@ -325,7 +325,8 @@ METHOD(private_key_t, sign, bool,
+ 	}
+ 
+ 	/* compute message representative mu */
+-	seed = chunk_cat("cmc", tr, pqc_params.pre_ctx, data);
++	seed = chunk_cat("ccmc", tr, chunk_from_thing(pre_hash), pqc_params.pre_ctx,
++					 data);
+ 	if (!this->H->set_seed(this->H, seed) ||
+ 		!this->H->get_bytes(this->H, ML_DSA_MU_LEN, mu.ptr))
+ 	{
+diff --git a/src/libstrongswan/plugins/ml/ml_dsa_public_key.c b/src/libstrongswan/plugins/ml/ml_dsa_public_key.c
+index 2d5d5f4bb5b..da7dc8dc769 100644
+--- a/src/libstrongswan/plugins/ml/ml_dsa_public_key.c
++++ b/src/libstrongswan/plugins/ml/ml_dsa_public_key.c
+@@ -338,6 +338,7 @@ METHOD(public_key_t, verify, bool,
+ 	private_public_key_t *this, signature_scheme_t scheme,
+ 	void *params, chunk_t data, chunk_t signature)
+ {
++	const u_char pre_hash = 0;
+ 	const u_int k = this->params->k;
+ 	const u_int l = this->params->l;
+ 	const int32_t bound = (1 << this->params->gamma1_exp) - this->params->beta;
+@@ -381,7 +382,8 @@ METHOD(public_key_t, verify, bool,
+ 	}
+ 
+ 	/* compute message representative mu */
+-	seed = chunk_cat("cmc", tr, pqc_params.pre_ctx, data);
++	seed = chunk_cat("ccmc", tr, chunk_from_thing(pre_hash), pqc_params.pre_ctx,
++					 data);
+ 	if (!this->H->set_seed(this->H, seed) ||
+ 		!this->H->get_bytes(this->H, ML_DSA_MU_LEN, mu.ptr))
+ 	{

diff --git a/strongswan.spec b/strongswan.spec
index cdc7619..e10c3cc 100644
--- a/strongswan.spec
+++ b/strongswan.spec
@@ -25,6 +25,7 @@ Source3:        tmpfiles-strongswan.conf
 Patch1:         strongswan-5.9.7-error-no-format.patch
 # Use isolation to prevent pip attempting to download during build
 Patch2:         strongswan-6.0.2-no-isolation.patch
+Patch3:         strongswan-mldsa.patch
 
 BuildRequires:  autoconf
 BuildRequires:  automake

                 reply	other threads:[~2026-08-10 14:02 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178637056664.1.3369289560107150870.rpms-strongswan-f7dc5ca86a43@fedoraproject.org \
    --to=paul.wouters@aiven.io \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox