415 lines
12 KiB
C++
415 lines
12 KiB
C++
// Parses MIKEY data (from Base64)
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <Base64.hh>
|
|
#include <NetCommon.h>
|
|
|
|
static u_int32_t get4Bytes(u_int8_t const*& ptr) {
|
|
u_int32_t result = (ptr[0]<<24)|(ptr[1]<<16)|(ptr[2]<<8)|ptr[3];
|
|
ptr += 4;
|
|
return result;
|
|
}
|
|
|
|
static u_int16_t get2Bytes(u_int8_t const*& ptr) {
|
|
u_int16_t result = (ptr[0]<<8)|ptr[1];
|
|
ptr += 2;
|
|
return result;
|
|
}
|
|
|
|
static u_int8_t getByte(u_int8_t const*& ptr) {
|
|
u_int8_t result = ptr[0];
|
|
ptr += 1;
|
|
return result;
|
|
}
|
|
|
|
Boolean parseMikeyUnknown(u_int8_t const*& /*ptr*/, u_int8_t const* /*endPtr*/, u_int8_t& /*nextPayloadType*/) {
|
|
fprintf(stderr, "\tUnknown or unhandled payload type\n");
|
|
return False;
|
|
}
|
|
|
|
char const* payloadTypeName[256];
|
|
char const* dataTypeComment[256];
|
|
#define testSize(n) do {if (ptr + (n) > endPtr) return False; } while (0)
|
|
|
|
Boolean parseMikeyHDR(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
|
|
testSize(10); // up to the start of "CS ID map info"
|
|
|
|
fprintf(stderr, "\tversion: %d\n", getByte(ptr));
|
|
|
|
u_int8_t const dataType = getByte(ptr);
|
|
fprintf(stderr, "\tdata type: %d (%s)\n", dataType, dataTypeComment[dataType]);
|
|
|
|
nextPayloadType = getByte(ptr);
|
|
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
|
|
|
|
u_int8_t const V_PRF = getByte(ptr);
|
|
u_int8_t const PRF = V_PRF&0x7F;
|
|
fprintf(stderr, "\tV:%d; PRF:%d (%s)\n", V_PRF>>7, PRF, PRF == 0 ? "MIKEY-1" : "unknown");
|
|
|
|
fprintf(stderr, "\tCSB ID:0x%08x\n", get4Bytes(ptr));
|
|
|
|
u_int8_t numCryptoSessions = getByte(ptr);
|
|
fprintf(stderr, "\t#CS:%d\n", numCryptoSessions);
|
|
|
|
u_int8_t const CS_ID_map_type = getByte(ptr);
|
|
fprintf(stderr, "\tCS ID map type:%d (%s)\n",
|
|
CS_ID_map_type, CS_ID_map_type == 0 ? "SRTP-ID" : "unknown");
|
|
if (CS_ID_map_type != 0) return False;
|
|
|
|
fprintf(stderr, "\tCS ID map info:\n");
|
|
testSize(numCryptoSessions * (1+4+4)); // the size of the "CS ID map info"
|
|
for (u_int8_t i = 1; i <= numCryptoSessions; ++i) {
|
|
fprintf(stderr, "\tPolicy_no_%d: %d;\tSSRC_%d: 0x%08x; ROC_%d: 0x%08x\n",
|
|
i, getByte(ptr),
|
|
i, get4Bytes(ptr),
|
|
i, get4Bytes(ptr));
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
static Boolean parseKeyDataSubPayload(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
|
|
fprintf(stderr, "\tEncr data:\n");
|
|
testSize(4); // up to the start of "Key data"
|
|
|
|
nextPayloadType = getByte(ptr);
|
|
fprintf(stderr, "\t\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
|
|
|
|
u_int8_t Type_KV = getByte(ptr);
|
|
u_int8_t Type = Type_KV>>4;
|
|
u_int8_t KV = Type_KV&0x0F;
|
|
fprintf(stderr, "\t\tType: %d (%s)\n", Type,
|
|
Type == 0 ? "TGK" : Type == 1 ? "TGK+SALT" : Type == 2 ? "TEK" : Type == 3 ? "TEK+SALT" : "unknown");
|
|
if (Type > 3) return False;
|
|
Boolean hasSalt = Type == 1 || Type == 3;
|
|
|
|
fprintf(stderr, "\t\tKey Validity: %d (%s)\n", KV,
|
|
KV == 0 ? "NULL" : KV == 1 ? "SPI/MKI" : KV == 2 ? "Interval" : "unknown");
|
|
Boolean hasKV = KV != 0;
|
|
|
|
u_int16_t keyDataLen = get2Bytes(ptr);
|
|
fprintf(stderr, "\t\tKey data len: %d\n", keyDataLen);
|
|
|
|
testSize(keyDataLen);
|
|
fprintf(stderr, "\t\tKey data: ");
|
|
for (unsigned i = 0; i < keyDataLen; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
|
|
if (hasSalt) {
|
|
testSize(2);
|
|
u_int16_t saltLen = get2Bytes(ptr);
|
|
fprintf(stderr, "\t\tSalt len: %d\n", saltLen);
|
|
|
|
testSize(saltLen);
|
|
fprintf(stderr, "\t\tSalt data: ");
|
|
for (unsigned i = 0; i < saltLen; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
if (hasKV) {
|
|
fprintf(stderr, "\t\tKV (key validity) data:\n");
|
|
if (KV == 1) { // SPI/MKI
|
|
testSize(1);
|
|
u_int8_t SPILength = getByte(ptr);
|
|
fprintf(stderr, "\t\t\tSPI Length: %d\n", SPILength);
|
|
|
|
testSize(SPILength);
|
|
fprintf(stderr, "\t\t\tSPI: ");
|
|
for (unsigned i = 0; i < SPILength; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
} else if (KV == 2) { // Interval
|
|
testSize(1);
|
|
u_int8_t VFLength = getByte(ptr);
|
|
fprintf(stderr, "\t\t\tVF Length: %d\n", VFLength);
|
|
|
|
testSize(VFLength);
|
|
fprintf(stderr, "\t\t\tVF: ");
|
|
for (unsigned i = 0; i < VFLength; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
|
|
testSize(1);
|
|
u_int8_t VTLength = getByte(ptr);
|
|
fprintf(stderr, "\t\t\tVT Length: %d\n", VTLength);
|
|
|
|
testSize(VTLength);
|
|
fprintf(stderr, "\t\t\tVT: ");
|
|
for (unsigned i = 0; i < VTLength; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
Boolean parseMikeyKEMAC(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
|
|
testSize(4); // up to the start of "Encr data"
|
|
|
|
nextPayloadType = getByte(ptr);
|
|
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
|
|
|
|
u_int8_t encrAlg = getByte(ptr);
|
|
fprintf(stderr, "\tEncr alg: %d (%s)\n", encrAlg,
|
|
encrAlg == 0 ? "NULL" : encrAlg == 1 ? "AES-CM-128" : encrAlg == 2 ? "AES-KW-128" : "unknown");
|
|
|
|
u_int16_t encrDataLen = get2Bytes(ptr);
|
|
fprintf(stderr, "\tencr data len: %d\n", encrDataLen);
|
|
|
|
testSize(encrDataLen + 1/*allow for "Mac alg"*/);
|
|
u_int8_t const* endOfKeyData = ptr + encrDataLen;
|
|
|
|
// Allow for multiple key data sub-payloads
|
|
while (ptr < endOfKeyData) {
|
|
if (!parseKeyDataSubPayload(ptr, endOfKeyData, nextPayloadType)) return False;
|
|
}
|
|
|
|
u_int8_t macAlg = getByte(ptr);
|
|
fprintf(stderr, "\tMAC alg: %d (%s)\n", macAlg,
|
|
macAlg == 0 ? "NULL" : macAlg == 1 ? "HMAC-SHA-1-160" : "unknown");
|
|
if (macAlg > 1) return False;
|
|
if (macAlg == 1) { // HMAC-SHA-1-160
|
|
unsigned const macLen = 160/8; // bytes
|
|
fprintf(stderr, "\t\tMAC: ");
|
|
for (unsigned i = 0; i < macLen; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
Boolean parseMikeyT(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
|
|
testSize(2); // up to the start of "TS value"
|
|
|
|
nextPayloadType = getByte(ptr);
|
|
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
|
|
|
|
u_int8_t TS_type = getByte(ptr);
|
|
unsigned TS_value_len;
|
|
fprintf(stderr, "\tTS type: %d (", TS_type);
|
|
switch (TS_type) {
|
|
case 0: {
|
|
fprintf(stderr, "NTP-UTC)\n");
|
|
TS_value_len = 8; // 64 bits
|
|
break;
|
|
}
|
|
case 1: {
|
|
fprintf(stderr, "NTP)\n");
|
|
TS_value_len = 8; // 64 bits
|
|
break;
|
|
}
|
|
case 2: {
|
|
fprintf(stderr, "COUNTER)\n");
|
|
TS_value_len = 4; // 32 bits
|
|
break;
|
|
}
|
|
default: {
|
|
fprintf(stderr, "unknown)\n");
|
|
return False;
|
|
}
|
|
}
|
|
|
|
testSize(TS_value_len);
|
|
fprintf(stderr, "\tTS value:");
|
|
for (unsigned i = 0; i < TS_value_len; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
|
|
return True;
|
|
}
|
|
|
|
#define MAX_SRTP_POLICY_PARAM_TYPE 12
|
|
static char const* SRTPPolicyParamTypeExplanation[] = {
|
|
"Encryption algorithm",
|
|
"Session Encryption key length",
|
|
"Authentication algorithm",
|
|
"Session Authentication key length",
|
|
"Session Salt key length",
|
|
"SRTP Pseudo Random Function",
|
|
"Key derivation rate",
|
|
"SRTP encryption off/on",
|
|
"SRTCP encryption off/on",
|
|
"Sender's FEC order",
|
|
"SRTP authentication off/on",
|
|
"Authentication tag length",
|
|
"SRTP prefix length",
|
|
};
|
|
|
|
static Boolean parseSRTPPolicyParam(u_int8_t const*& ptr, u_int8_t const* endPtr) {
|
|
fprintf(stderr, "\tPolicy param:\n");
|
|
while (ptr < endPtr) {
|
|
testSize(2);
|
|
|
|
u_int8_t ppType = getByte(ptr);
|
|
fprintf(stderr, "\t\ttype: %d (%s); ", ppType,
|
|
ppType > MAX_SRTP_POLICY_PARAM_TYPE ? "unknown" : SRTPPolicyParamTypeExplanation[ppType]);
|
|
|
|
u_int8_t ppLen = getByte(ptr);
|
|
fprintf(stderr, "length: %d; value: ", ppLen);
|
|
|
|
testSize(ppLen);
|
|
u_int8_t ppVal = 0xFF;
|
|
if (ppLen == 1) {
|
|
ppVal = getByte(ptr);
|
|
fprintf(stderr, "%d", ppVal);
|
|
} else {
|
|
for (unsigned j = 0; j < ppLen; ++j) fprintf(stderr, ":%02x", getByte(ptr));
|
|
}
|
|
|
|
switch (ppType) {
|
|
case 0: { // Encryption algorithm
|
|
fprintf(stderr, " (%s)",
|
|
ppVal == 0 ? "NULL" : ppVal == 1 ? "AES-CM" : ppVal == 2 ? "AES-F8" : "unknown");
|
|
break;
|
|
}
|
|
case 2: { // Authentication algorithm
|
|
fprintf(stderr, " (%s)",
|
|
ppVal == 0 ? "NULL" : ppVal == 1 ? "HMAC-SHA-1" : "unknown");
|
|
break;
|
|
}
|
|
case 5: { // SRTP Pseudo Random Function
|
|
fprintf(stderr, " (%s)",
|
|
ppVal == 0 ? "AES-CM" : "unknown");
|
|
break;
|
|
}
|
|
case 9: { // sender's FEC order
|
|
fprintf(stderr, " (%s)",
|
|
ppVal == 0 ? "First FEC, then SRTP" : "unknown");
|
|
break;
|
|
}
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
Boolean parseMikeySP(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
|
|
testSize(2); // up to the start of "Policy param"
|
|
|
|
nextPayloadType = getByte(ptr);
|
|
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
|
|
|
|
fprintf(stderr, "\tPolicy number: %d\n", getByte(ptr));
|
|
|
|
u_int8_t protocolType = getByte(ptr);
|
|
fprintf(stderr, "\tProtocol type: %d (%s)\n", protocolType, protocolType == 0 ? "SRTP" : "unknown");
|
|
if (protocolType != 0) return False;
|
|
|
|
u_int16_t policyParam_len = get2Bytes(ptr);
|
|
fprintf(stderr, "\tPolicy param len: %d\n", policyParam_len);
|
|
|
|
testSize(policyParam_len);
|
|
return parseSRTPPolicyParam(ptr, ptr + policyParam_len);
|
|
}
|
|
|
|
Boolean parseMikeyRAND(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
|
|
testSize(2); // up to the start of "RAND"
|
|
|
|
nextPayloadType = getByte(ptr);
|
|
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
|
|
|
|
u_int8_t RAND_len = getByte(ptr);
|
|
fprintf(stderr, "\tRAND len: %d", RAND_len);
|
|
|
|
testSize(RAND_len);
|
|
fprintf(stderr, "\tRAND:");
|
|
for (unsigned i = 0; i < RAND_len; ++i) fprintf(stderr, ":%02x", getByte(ptr));
|
|
fprintf(stderr, "\n");
|
|
|
|
return True;
|
|
}
|
|
|
|
typedef Boolean (parseMikeyPayloadFunc)(u_int8_t const*& ptr, u_int8_t const* endPtr,
|
|
u_int8_t& nextPayloadType);
|
|
parseMikeyPayloadFunc* payloadParser[256];
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s <base64Data>\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
char const* base64Data = argv[1];
|
|
|
|
unsigned mikeyDataSize;
|
|
u_int8_t* mikeyData = base64Decode(base64Data, mikeyDataSize);
|
|
|
|
fprintf(stderr, "Base64Data \"%s\" produces %d bytes of MIKEY data:\n", base64Data, mikeyDataSize);
|
|
for (unsigned i = 0; i < mikeyDataSize; ++i) fprintf(stderr, ":%02x", mikeyData[i]);
|
|
fprintf(stderr, "\n");
|
|
|
|
for (unsigned i = 0; i < 256; ++i) {
|
|
payloadTypeName[i] = "unknown or unhandled";
|
|
payloadParser[i] = parseMikeyUnknown;
|
|
|
|
dataTypeComment[i] = "unknown";
|
|
}
|
|
|
|
// Populate known payload types:
|
|
payloadTypeName[0] = "Last payload";
|
|
|
|
payloadTypeName[1] = "KEMAC";
|
|
payloadParser[1] = parseMikeyKEMAC;
|
|
|
|
payloadTypeName[2] = "PKE";
|
|
|
|
payloadTypeName[3] = "DH";
|
|
|
|
payloadTypeName[4] = "SIGN";
|
|
|
|
payloadTypeName[5] = "T";
|
|
payloadParser[5] = parseMikeyT;
|
|
|
|
payloadTypeName[6] = "ID";
|
|
|
|
payloadTypeName[7] = "CERT";
|
|
|
|
payloadTypeName[8] = "CHASH";
|
|
|
|
payloadTypeName[9] = "V";
|
|
|
|
payloadTypeName[10] = "SP";
|
|
payloadParser[10] = parseMikeySP;
|
|
|
|
payloadTypeName[11] = "RAND";
|
|
payloadParser[11] = parseMikeyRAND;
|
|
|
|
payloadTypeName[12] = "ERR";
|
|
|
|
payloadTypeName[20] = "Key data";
|
|
|
|
payloadTypeName[21] = "General Ext.";
|
|
|
|
// Populate known data types:
|
|
dataTypeComment[0] = "Initiator's pre-shared key message";
|
|
dataTypeComment[1] = "Verification message of a pre-shared key message";
|
|
dataTypeComment[2] = "Initiator's public-key transport message";
|
|
dataTypeComment[3] = "Verification message of a public-key message";
|
|
dataTypeComment[4] = "Initiator's DH exchange message";
|
|
dataTypeComment[5] = "Responder's DH exchange message";
|
|
dataTypeComment[6] = "Error message";
|
|
|
|
u_int8_t const* ptr = mikeyData;
|
|
u_int8_t* const endPtr = &mikeyData[mikeyDataSize];
|
|
u_int8_t nextPayloadType;
|
|
|
|
do {
|
|
// Begin by parsing an initial "HDR":
|
|
fprintf(stderr, "HDR:\n");
|
|
if (!parseMikeyHDR(ptr, endPtr, nextPayloadType)) break;
|
|
|
|
// Then parse each successive payload:
|
|
while (nextPayloadType != 0 /* Last payload */) {
|
|
fprintf(stderr, "%s:\n", payloadTypeName[nextPayloadType]);
|
|
if (!(*payloadParser[nextPayloadType])(ptr, endPtr, nextPayloadType)) break;
|
|
}
|
|
} while (0);
|
|
|
|
if (ptr < endPtr) {
|
|
fprintf(stderr, "+%ld bytes of unparsed data: ", endPtr-ptr);
|
|
while (ptr < endPtr) fprintf(stderr, ":%02x", *ptr++);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
delete[] mikeyData;
|
|
return 0;
|
|
}
|