// Parses MIKEY data (from Base64) #include #include #include #include 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 \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; }