Added a EAP-SIM/AKA backend reading triplets/quintuplets from a SQL database

This commit is contained in:
Martin Willi 2010-06-02 15:55:58 +02:00
parent c77f4b305e
commit d2c358742a
12 changed files with 685 additions and 0 deletions

View File

@ -101,6 +101,7 @@ ARG_ENABL_SET([unit-tests], [enable unit tests on IKEv2 daemon startup.])
ARG_ENABL_SET([load-tester], [enable load testing plugin for IKEv2 daemon.])
ARG_ENABL_SET([eap-sim], [enable SIM authenication module for EAP.])
ARG_ENABL_SET([eap-sim-file], [enable EAP-SIM backend based on a triplet file.])
ARG_ENABL_SET([eap-simaka-sql], [enable EAP-SIM/AKA backend based on a triplet/quintuplet SQL database.])
ARG_ENABL_SET([eap-simaka-pseudonym], [enable EAP-SIM/AKA pseudonym storage plugin.])
ARG_ENABL_SET([eap-simaka-reauth], [enable EAP-SIM/AKA reauthentication data storage plugin.])
ARG_ENABL_SET([eap-identity], [enable EAP module providing EAP-Identity helper.])
@ -815,6 +816,7 @@ AM_CONDITIONAL(USE_LOAD_TESTER, test x$load_tester = xtrue)
AM_CONDITIONAL(USE_HA, test x$ha = xtrue)
AM_CONDITIONAL(USE_EAP_SIM, test x$eap_sim = xtrue)
AM_CONDITIONAL(USE_EAP_SIM_FILE, test x$eap_sim_file = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_SQL, test x$eap_simaka_sql = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_PSEUDONYM, test x$eap_simaka_pseudonym = xtrue)
AM_CONDITIONAL(USE_EAP_SIMAKA_REAUTH, test x$eap_simaka_reauth = xtrue)
AM_CONDITIONAL(USE_EAP_IDENTITY, test x$eap_identity = xtrue)
@ -939,6 +941,7 @@ AC_OUTPUT(
src/libcharon/plugins/eap_gtc/Makefile
src/libcharon/plugins/eap_sim/Makefile
src/libcharon/plugins/eap_sim_file/Makefile
src/libcharon/plugins/eap_simaka_sql/Makefile
src/libcharon/plugins/eap_simaka_pseudonym/Makefile
src/libcharon/plugins/eap_simaka_reauth/Makefile
src/libcharon/plugins/eap_mschapv2/Makefile

View File

@ -129,6 +129,8 @@ LOCAL_SRC_FILES += $(call add_plugin, eap-mschapv2)
LOCAL_SRC_FILES += $(call add_plugin, eap-sim)
LOCAL_SRC_FILES += $(call add_plugin, eap-simaka-sql)
LOCAL_SRC_FILES += $(call add_plugin, eap-simaka-pseudonym)
LOCAL_SRC_FILES += $(call add_plugin, eap-simaka-reauth)

View File

@ -271,6 +271,14 @@ if MONOLITHIC
endif
endif
if USE_EAP_SIMAKA_SQL
SUBDIRS += plugins/eap_simaka_sql
PLUGINS += eap-simaka-sql
if MONOLITHIC
libcharon_la_LIBADD += plugins/eap_simaka_sql/libstrongswan-eap-simaka-sql.la
endif
endif
if USE_EAP_SIMAKA_PSEUDONYM
SUBDIRS += plugins/eap_simaka_pseudonym
PLUGINS += eap-simaka-pseudonym

View File

@ -0,0 +1,18 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
-I$(top_srcdir)/src/libcharon
AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${sysconfdir}\"
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-simaka-sql.la
else
plugin_LTLIBRARIES = libstrongswan-eap-simaka-sql.la
endif
libstrongswan_eap_simaka_sql_la_SOURCES = \
eap_simaka_sql_plugin.h eap_simaka_sql_plugin.c \
eap_simaka_sql_card.h eap_simaka_sql_card.c \
eap_simaka_sql_provider.h eap_simaka_sql_provider.c
libstrongswan_eap_simaka_sql_la_LDFLAGS = -module -avoid-version

View File

@ -0,0 +1,177 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec 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 "eap_simaka_sql_card.h"
#include <time.h>
#include <daemon.h>
typedef struct private_eap_simaka_sql_card_t private_eap_simaka_sql_card_t;
/**
* Private data of an eap_simaka_sql_card_t object.
*/
struct private_eap_simaka_sql_card_t {
/**
* Public eap_simaka_sql_card_t interface.
*/
eap_simaka_sql_card_t public;
/**
* Triplet/quintuplet database
*/
database_t *db;
/**
* Remove used triplets/quintuplets from database
*/
bool remove_used;
};
METHOD(sim_card_t, get_triplet, bool,
private_eap_simaka_sql_card_t *this, identification_t *id,
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
{
chunk_t sres_chunk, kc_chunk;
enumerator_t *query;
bool found = FALSE;
char buf[128];
snprintf(buf, sizeof(buf), "%Y", id);
query = this->db->query(this->db,
"select sres, kc from triplets where rand = ? and id = ? "
"order by use limit 1",
DB_BLOB, chunk_create(rand, SIM_RAND_LEN), DB_TEXT, buf,
DB_BLOB, DB_BLOB);
if (query)
{
if (query->enumerate(query, &sres_chunk, &kc_chunk))
{
if (sres_chunk.len == SIM_SRES_LEN &&
kc_chunk.len == SIM_KC_LEN)
{
memcpy(sres, sres_chunk.ptr, SIM_SRES_LEN);
memcpy(kc, kc_chunk.ptr, SIM_KC_LEN);
found = TRUE;
}
}
query->destroy(query);
}
if (found)
{
if (this->remove_used)
{
this->db->execute(this->db, NULL,
"delete from triplets where id = ? and rand = ?",
DB_TEXT, buf, DB_BLOB, chunk_create(rand, SIM_RAND_LEN));
}
else
{
this->db->execute(this->db, NULL,
"update triplets set use = ? where id = ? and rand = ?",
DB_UINT, time(NULL), DB_TEXT, buf,
DB_BLOB, chunk_create(rand, SIM_RAND_LEN));
}
}
return found;
}
METHOD(sim_card_t, get_quintuplet, status_t,
private_eap_simaka_sql_card_t *this, identification_t *id,
char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len)
{
chunk_t ck_chunk, ik_chunk, res_chunk;
enumerator_t *query;
status_t found = FAILED;
char buf[128];
snprintf(buf, sizeof(buf), "%Y", id);
query = this->db->query(this->db, "select ck, ik, res from quintuplets "
"where rand = ? and autn = ? and id = ? order by use limit 1",
DB_BLOB, chunk_create(rand, AKA_RAND_LEN),
DB_BLOB, chunk_create(autn, AKA_AUTN_LEN), DB_TEXT, buf,
DB_BLOB, DB_BLOB, DB_BLOB);
if (query)
{
if (query->enumerate(query, &ck_chunk, &ik_chunk, &res_chunk))
{
if (ck_chunk.len == AKA_CK_LEN &&
ik_chunk.len == AKA_IK_LEN &&
res_chunk.len <= AKA_RES_MAX)
{
memcpy(ck, ck_chunk.ptr, AKA_CK_LEN);
memcpy(ik, ik_chunk.ptr, AKA_IK_LEN);
memcpy(res, res_chunk.ptr, res_chunk.len);
*res_len = res_chunk.len;
found = SUCCESS;
}
}
query->destroy(query);
}
if (found == SUCCESS)
{
if (this->remove_used)
{
this->db->execute(this->db, NULL,
"delete from quintuplets where id = ? and rand = ?",
DB_TEXT, buf, DB_BLOB, chunk_create(rand, SIM_RAND_LEN));
}
else
{
this->db->execute(this->db, NULL,
"update quintuplets set use = ? where id = ? and rand = ?",
DB_UINT, time(NULL), DB_TEXT, buf,
DB_BLOB, chunk_create(rand, AKA_RAND_LEN));
}
}
return found;
}
METHOD(eap_simaka_sql_card_t, destroy, void,
private_eap_simaka_sql_card_t *this)
{
free(this);
}
/**
* See header
*/
eap_simaka_sql_card_t *eap_simaka_sql_card_create(database_t *db,
bool remove_used)
{
private_eap_simaka_sql_card_t *this;
INIT(this,
.public = {
.card = {
.get_triplet = _get_triplet,
.get_quintuplet = _get_quintuplet,
.resync = (void*)return_false,
.get_pseudonym = (void*)return_null,
.set_pseudonym = (void*)nop,
.get_reauth = (void*)return_null,
.set_reauth = (void*)nop,
},
.destroy = _destroy,
},
.db = db,
.remove_used = remove_used,
);
return &this->public;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec 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 eap_simaka_sql_card eap_simaka_sql_card
* @{ @ingroup eap_simaka_sql
*/
#ifndef EAP_SIMAKA_SQL_CARD_H_
#define EAP_SIMAKA_SQL_CARD_H_
#include <database/database.h>
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_simaka_sql_card_t eap_simaka_sql_card_t;
/**
* SIM card implementation using a triplet/quintuplet database backend.
*/
struct eap_simaka_sql_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_simaka_sql_card_t.
*/
void (*destroy)(eap_simaka_sql_card_t *this);
};
/**
* Create a eap_simaka_sql_card instance.
*
* @param db triplet/quintuplet database
* @param remove_used TRUE to remove used triplets/quintuplets from db
*/
eap_simaka_sql_card_t *eap_simaka_sql_card_create(database_t *db,
bool remove_used);
#endif /** EAP_SIMAKA_SQL_CARD_H_ @}*/

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec 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 "eap_simaka_sql_plugin.h"
#include "eap_simaka_sql_card.h"
#include "eap_simaka_sql_provider.h"
#include <daemon.h>
typedef struct private_eap_simaka_sql_t private_eap_simaka_sql_t;
/**
* Private data of an eap_simaka_sql_t object.
*/
struct private_eap_simaka_sql_t {
/**
* Public eap_simaka_sql_plugin_t interface.
*/
eap_simaka_sql_plugin_t public;
/**
* (U)SIM card
*/
eap_simaka_sql_card_t *card;
/**
* (U)SIM provider
*/
eap_simaka_sql_provider_t *provider;
/**
* Database with triplets/quintuplets
*/
database_t *db;
};
METHOD(plugin_t, destroy, void,
private_eap_simaka_sql_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
charon->sim->remove_provider(charon->sim, &this->provider->provider);
this->card->destroy(this->card);
this->provider->destroy(this->provider);
this->db->destroy(this->db);
free(this);
}
/**
* See header
*/
plugin_t *eap_simaka_sql_plugin_create()
{
private_eap_simaka_sql_t *this;
database_t *db;
bool remove_used;
char *uri;
uri = lib->settings->get_str(lib->settings,
"charon.plugins.eap-simaka-sql.database", NULL);
if (!uri)
{
DBG1(DBG_CFG, "eap-simaka-sql database URI missing");
return NULL;
}
db = lib->db->create(lib->db, uri);
if (!db)
{
DBG1(DBG_CFG, "opening eap-simaka-sql database failed");
return NULL;
}
remove_used = lib->settings->get_bool(lib->settings,
"charon.plugins.eap-simaka-sql.remove_used", FALSE);
INIT(this,
.public.plugin = {
.destroy = _destroy,
},
.db = db,
.provider = eap_simaka_sql_provider_create(db, remove_used),
.card = eap_simaka_sql_card_create(db, remove_used),
);
charon->sim->add_card(charon->sim, &this->card->card);
charon->sim->add_provider(charon->sim, &this->provider->provider);
return &this->public.plugin;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec 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 eap_simaka_sql eap_simaka_sql
* @ingroup cplugins
*
* @defgroup eap_simaka_sql_plugin eap_simaka_sql_plugin
* @{ @ingroup eap_simaka_sql
*/
#ifndef EAP_SIMAKA_SQL_PLUGIN_H_
#define EAP_SIMAKA_SQL_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_simaka_sql_plugin_t eap_simaka_sql_plugin_t;
/**
* Plugin to provide SIM/AKA cards/providers using triplets from a database.
*/
struct eap_simaka_sql_plugin_t {
/**
* Implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIMAKA_SQL_PLUGIN_H_ @}*/

View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec 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 "eap_simaka_sql_provider.h"
#include <time.h>
#include <daemon.h>
typedef struct private_eap_simaka_sql_provider_t private_eap_simaka_sql_provider_t;
/**
* Private data of an eap_simaka_sql_provider_t object.
*/
struct private_eap_simaka_sql_provider_t {
/**
* Public eap_simaka_sql_provider_t interface.
*/
eap_simaka_sql_provider_t public;
/**
* Triplet/quintuplet database
*/
database_t *db;
/**
* Remove used triplets/quintuplets from database
*/
bool remove_used;
};
METHOD(sim_provider_t, get_triplet, bool,
private_eap_simaka_sql_provider_t *this, identification_t *id,
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
{
chunk_t rand_chunk, sres_chunk, kc_chunk;
enumerator_t *query;
bool found = FALSE;
char buf[128];
snprintf(buf, sizeof(buf), "%Y", id);
query = this->db->query(this->db,
"select rand, sres, kc from triplets where id = ? order by use",
DB_TEXT, buf, DB_BLOB, DB_BLOB, DB_BLOB);
if (query)
{
if (query->enumerate(query, &rand_chunk, &sres_chunk, &kc_chunk))
{
if (rand_chunk.len == SIM_RAND_LEN &&
sres_chunk.len == SIM_SRES_LEN &&
kc_chunk.len == SIM_KC_LEN)
{
memcpy(rand, rand_chunk.ptr, SIM_RAND_LEN);
memcpy(sres, sres_chunk.ptr, SIM_SRES_LEN);
memcpy(kc, kc_chunk.ptr, SIM_KC_LEN);
found = TRUE;
}
}
query->destroy(query);
}
if (found)
{
if (this->remove_used)
{
this->db->execute(this->db, NULL,
"delete from triplets where id = ? and rand = ?",
DB_TEXT, buf, DB_BLOB, chunk_create(rand, SIM_RAND_LEN));
}
else
{
this->db->execute(this->db, NULL,
"update triplets set use = ? where id = ? and rand = ?",
DB_UINT, time(NULL), DB_TEXT, buf,
DB_BLOB, chunk_create(rand, SIM_RAND_LEN));
}
}
return found;
}
METHOD(sim_provider_t, get_quintuplet, bool,
private_eap_simaka_sql_provider_t *this, identification_t *id,
char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len,
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
{
chunk_t rand_chunk, xres_chunk, ck_chunk, ik_chunk, autn_chunk;
enumerator_t *query;
bool found = FALSE;
char buf[128];
snprintf(buf, sizeof(buf), "%Y", id);
query = this->db->query(this->db, "select rand, res, ck, ik, autn "
"from quintuplets where id = ? order by use", DB_TEXT, buf,
DB_BLOB, DB_BLOB, DB_BLOB, DB_BLOB, DB_BLOB);
if (query)
{
if (query->enumerate(query, &rand_chunk, &xres_chunk,
&ck_chunk, &ik_chunk, &autn_chunk))
{
if (rand_chunk.len == AKA_RAND_LEN &&
xres_chunk.len <= AKA_RES_MAX &&
ck_chunk.len == AKA_CK_LEN &&
ik_chunk.len == AKA_IK_LEN &&
autn_chunk.len == AKA_AUTN_LEN)
{
memcpy(rand, rand_chunk.ptr, AKA_RAND_LEN);
memcpy(xres, xres_chunk.ptr, xres_chunk.len);
*xres_len = xres_chunk.len;
memcpy(ck, ck_chunk.ptr, AKA_CK_LEN);
memcpy(ik, ik_chunk.ptr, AKA_IK_LEN);
memcpy(autn, autn_chunk.ptr, AKA_AUTN_LEN);
found = TRUE;
}
}
query->destroy(query);
}
if (found)
{
if (this->remove_used)
{
this->db->execute(this->db, NULL,
"delete from quintuplets where id = ? and rand = ?",
DB_TEXT, buf, DB_BLOB, chunk_create(rand, SIM_RAND_LEN));
}
else
{
this->db->execute(this->db, NULL,
"update quintuplets set use = ? where id = ? and rand = ?",
DB_UINT, time(NULL), DB_TEXT, buf,
DB_BLOB, chunk_create(rand, AKA_RAND_LEN));
}
}
return found;
}
METHOD(eap_simaka_sql_provider_t, destroy, void,
private_eap_simaka_sql_provider_t *this)
{
free(this);
}
/**
* See header
*/
eap_simaka_sql_provider_t *eap_simaka_sql_provider_create(database_t *db,
bool remove_used)
{
private_eap_simaka_sql_provider_t *this;
INIT(this,
.public = {
.provider = {
.get_triplet = _get_triplet,
.get_quintuplet = _get_quintuplet,
.resync = (void*)return_false,
.is_pseudonym = (void*)return_null,
.gen_pseudonym = (void*)return_null,
.is_reauth = (void*)return_null,
.gen_reauth = (void*)return_null,
},
.destroy = _destroy,
},
.db = db,
.remove_used = remove_used,
);
return &this->public;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 revosec 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 eap_simaka_sql_provider eap_simaka_sql_provider
* @{ @ingroup eap_simaka_sql
*/
#ifndef EAP_SIMAKA_SQL_PROVIDER_H_
#define EAP_SIMAKA_SQL_PROVIDER_H_
#include <database/database.h>
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_simaka_sql_provider_t eap_simaka_sql_provider_t;
/**
* SIM provider implementation using a triplet/quintuplet database backend.
*/
struct eap_simaka_sql_provider_t {
/**
* Implements sim_provider_t interface
*/
sim_provider_t provider;
/**
* Destroy a eap_simaka_sql_provider_t.
*/
void (*destroy)(eap_simaka_sql_provider_t *this);
};
/**
* Create a eap_simaka_sql_provider instance.
*
* @param db triplet/quintuplet database
* @param remove_used TRUE to remove used triplets/quintuplets from db
*/
eap_simaka_sql_provider_t *eap_simaka_sql_provider_create(database_t *db,
bool remove_used);
#endif /** EAP_SIMAKA_SQL_PROVIDER_H_ @}*/

View File

@ -0,0 +1,19 @@
DROP TABLE IF EXISTS triplets;
CREATE TABLE triplets (
id TEXT NOT NULL,
use INTEGER NOT NULL,
rand BLOB NOT NULL,
sres BLOB NOT NULL,
kc BLOB NOT NULL
);
DROP TABLE IF EXISTS quintuplets;
CREATE TABLE quintuplets (
id TEXT NOT NULL,
use INTEGER NOT NULL,
rand BLOB NOT NULL,
autn BLOB NOT NULL,
ck BLOB NOT NULL,
ik BLOB NOT NULL,
res BLOB NOT NULL
);

View File

@ -0,0 +1,28 @@
DELETE FROM triplets;
DELETE FROM quintuplets;
INSERT INTO triplets
(id, use, rand, sres, kc) VALUES
('moon@strongswan.org', 0,
X'00112233445566778899AABBCCDDEEFF', X'01234567', X'0123456789ABCDEF'
);
INSERT INTO triplets
(id, use, rand, sres, kc) VALUES
('moon@strongswan.org', 0,
X'112233445566778899AABBCCDDEEFF00', X'12345678', X'123456789ABCDEF0'
);
INSERT INTO triplets
(id, use, rand, sres, kc) VALUES
('moon@strongswan.org', 0,
X'2233445566778899AABBCCDDEEFF0011', X'23456789', X'23456789ABCDEF01'
);
INSERT INTO quintuplets
(id, use, rand, autn, ck, ik, res) VALUES
('moon@strongswan.org', 0,
X'00112233445566778899AABBCCDDEEFF',
X'112233445566778899AABBCCDDEEFF00',
X'2233445566778899AABBCCDDEEFF0011',
X'33445566778899AABBCCDDEEFF001122',
X'00112233445566778899'
);