// OpenSSL-used Cipher Suite String\r
//\r
CONST CHAR8 *OpensslCipher;\r
-} TLS_CIPHER_PAIR;\r
+ //\r
+ // Length of OpensslCipher\r
+ //\r
+ UINTN OpensslCipherLength;\r
+} TLS_CIPHER_MAPPING;\r
+\r
+//\r
+// Create a TLS_CIPHER_MAPPING initializer from IanaCipher and OpensslCipher so\r
+// that OpensslCipherLength is filled in automatically. IanaCipher must be an\r
+// integer constant expression, and OpensslCipher must be a string literal.\r
+//\r
+#define MAP(IanaCipher, OpensslCipher) \\r
+ { (IanaCipher), (OpensslCipher), sizeof (OpensslCipher) - 1 }\r
\r
//\r
// The mapping table between IANA/IETF Cipher Suite definitions and\r
// OpenSSL-used Cipher Suite name.\r
//\r
-STATIC CONST TLS_CIPHER_PAIR TlsCipherMappingTable[] = {\r
- { 0x0001, "NULL-MD5" }, /// TLS_RSA_WITH_NULL_MD5\r
- { 0x0002, "NULL-SHA" }, /// TLS_RSA_WITH_NULL_SHA\r
- { 0x0004, "RC4-MD5" }, /// TLS_RSA_WITH_RC4_128_MD5\r
- { 0x0005, "RC4-SHA" }, /// TLS_RSA_WITH_RC4_128_SHA\r
- { 0x000A, "DES-CBC3-SHA" }, /// TLS_RSA_WITH_3DES_EDE_CBC_SHA, mandatory TLS 1.1\r
- { 0x0016, "DHE-RSA-DES-CBC3-SHA" }, /// TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA\r
- { 0x002F, "AES128-SHA" }, /// TLS_RSA_WITH_AES_128_CBC_SHA, mandatory TLS 1.2\r
- { 0x0030, "DH-DSS-AES128-SHA" }, /// TLS_DH_DSS_WITH_AES_128_CBC_SHA\r
- { 0x0031, "DH-RSA-AES128-SHA" }, /// TLS_DH_RSA_WITH_AES_128_CBC_SHA\r
- { 0x0033, "DHE-RSA-AES128-SHA" }, /// TLS_DHE_RSA_WITH_AES_128_CBC_SHA\r
- { 0x0035, "AES256-SHA" }, /// TLS_RSA_WITH_AES_256_CBC_SHA\r
- { 0x0036, "DH-DSS-AES256-SHA" }, /// TLS_DH_DSS_WITH_AES_256_CBC_SHA\r
- { 0x0037, "DH-RSA-AES256-SHA" }, /// TLS_DH_RSA_WITH_AES_256_CBC_SHA\r
- { 0x0039, "DHE-RSA-AES256-SHA" }, /// TLS_DHE_RSA_WITH_AES_256_CBC_SHA\r
- { 0x003B, "NULL-SHA256" }, /// TLS_RSA_WITH_NULL_SHA256\r
- { 0x003C, "AES128-SHA256" }, /// TLS_RSA_WITH_AES_128_CBC_SHA256\r
- { 0x003D, "AES256-SHA256" }, /// TLS_RSA_WITH_AES_256_CBC_SHA256\r
- { 0x003E, "DH-DSS-AES128-SHA256" }, /// TLS_DH_DSS_WITH_AES_128_CBC_SHA256\r
- { 0x003F, "DH-RSA-AES128-SHA256" }, /// TLS_DH_RSA_WITH_AES_128_CBC_SHA256\r
- { 0x0067, "DHE-RSA-AES128-SHA256" }, /// TLS_DHE_RSA_WITH_AES_128_CBC_SHA256\r
- { 0x0068, "DH-DSS-AES256-SHA256" }, /// TLS_DH_DSS_WITH_AES_256_CBC_SHA256\r
- { 0x0069, "DH-RSA-AES256-SHA256" }, /// TLS_DH_RSA_WITH_AES_256_CBC_SHA256\r
- { 0x006B, "DHE-RSA-AES256-SHA256" } /// TLS_DHE_RSA_WITH_AES_256_CBC_SHA256\r
+// Keep the table uniquely sorted by the IanaCipher field, in increasing order.\r
+//\r
+STATIC CONST TLS_CIPHER_MAPPING TlsCipherMappingTable[] = {\r
+ MAP ( 0x0001, "NULL-MD5" ), /// TLS_RSA_WITH_NULL_MD5\r
+ MAP ( 0x0002, "NULL-SHA" ), /// TLS_RSA_WITH_NULL_SHA\r
+ MAP ( 0x0004, "RC4-MD5" ), /// TLS_RSA_WITH_RC4_128_MD5\r
+ MAP ( 0x0005, "RC4-SHA" ), /// TLS_RSA_WITH_RC4_128_SHA\r
+ MAP ( 0x000A, "DES-CBC3-SHA" ), /// TLS_RSA_WITH_3DES_EDE_CBC_SHA, mandatory TLS 1.1\r
+ MAP ( 0x0016, "DHE-RSA-DES-CBC3-SHA" ), /// TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA\r
+ MAP ( 0x002F, "AES128-SHA" ), /// TLS_RSA_WITH_AES_128_CBC_SHA, mandatory TLS 1.2\r
+ MAP ( 0x0030, "DH-DSS-AES128-SHA" ), /// TLS_DH_DSS_WITH_AES_128_CBC_SHA\r
+ MAP ( 0x0031, "DH-RSA-AES128-SHA" ), /// TLS_DH_RSA_WITH_AES_128_CBC_SHA\r
+ MAP ( 0x0033, "DHE-RSA-AES128-SHA" ), /// TLS_DHE_RSA_WITH_AES_128_CBC_SHA\r
+ MAP ( 0x0035, "AES256-SHA" ), /// TLS_RSA_WITH_AES_256_CBC_SHA\r
+ MAP ( 0x0036, "DH-DSS-AES256-SHA" ), /// TLS_DH_DSS_WITH_AES_256_CBC_SHA\r
+ MAP ( 0x0037, "DH-RSA-AES256-SHA" ), /// TLS_DH_RSA_WITH_AES_256_CBC_SHA\r
+ MAP ( 0x0039, "DHE-RSA-AES256-SHA" ), /// TLS_DHE_RSA_WITH_AES_256_CBC_SHA\r
+ MAP ( 0x003B, "NULL-SHA256" ), /// TLS_RSA_WITH_NULL_SHA256\r
+ MAP ( 0x003C, "AES128-SHA256" ), /// TLS_RSA_WITH_AES_128_CBC_SHA256\r
+ MAP ( 0x003D, "AES256-SHA256" ), /// TLS_RSA_WITH_AES_256_CBC_SHA256\r
+ MAP ( 0x003E, "DH-DSS-AES128-SHA256" ), /// TLS_DH_DSS_WITH_AES_128_CBC_SHA256\r
+ MAP ( 0x003F, "DH-RSA-AES128-SHA256" ), /// TLS_DH_RSA_WITH_AES_128_CBC_SHA256\r
+ MAP ( 0x0067, "DHE-RSA-AES128-SHA256" ), /// TLS_DHE_RSA_WITH_AES_128_CBC_SHA256\r
+ MAP ( 0x0068, "DH-DSS-AES256-SHA256" ), /// TLS_DH_DSS_WITH_AES_256_CBC_SHA256\r
+ MAP ( 0x0069, "DH-RSA-AES256-SHA256" ), /// TLS_DH_RSA_WITH_AES_256_CBC_SHA256\r
+ MAP ( 0x006B, "DHE-RSA-AES256-SHA256" ), /// TLS_DHE_RSA_WITH_AES_256_CBC_SHA256\r
};\r
\r
/**\r
- Gets the OpenSSL cipher suite string for the supplied IANA TLS cipher suite.\r
+ Gets the OpenSSL cipher suite mapping for the supplied IANA TLS cipher suite.\r
\r
@param[in] CipherId The supplied IANA TLS cipher suite ID.\r
\r
- @return The corresponding OpenSSL cipher suite string if found,\r
+ @return The corresponding OpenSSL cipher suite mapping if found,\r
NULL otherwise.\r
\r
**/\r
STATIC\r
-CONST CHAR8 *\r
-TlsGetCipherString (\r
+CONST TLS_CIPHER_MAPPING *\r
+TlsGetCipherMapping (\r
IN UINT16 CipherId\r
)\r
{\r
- CONST TLS_CIPHER_PAIR *CipherEntry;\r
- UINTN TableSize;\r
- UINTN Index;\r
-\r
- CipherEntry = TlsCipherMappingTable;\r
- TableSize = sizeof (TlsCipherMappingTable) / sizeof (TLS_CIPHER_PAIR);\r
+ INTN Left;\r
+ INTN Right;\r
+ INTN Middle;\r
\r
//\r
- // Search Cipher Mapping Table for IANA-OpenSSL Cipher Translation\r
+ // Binary Search Cipher Mapping Table for IANA-OpenSSL Cipher Translation\r
//\r
- for (Index = 0; Index < TableSize; Index++, CipherEntry++) {\r
- //\r
- // Translate IANA cipher suite name to OpenSSL name.\r
- //\r
- if (CipherEntry->IanaCipher == CipherId) {\r
- return CipherEntry->OpensslCipher;\r
+ Left = 0;\r
+ Right = ARRAY_SIZE (TlsCipherMappingTable) - 1;\r
+\r
+ while (Right >= Left) {\r
+ Middle = (Left + Right) / 2;\r
+\r
+ if (CipherId == TlsCipherMappingTable[Middle].IanaCipher) {\r
+ //\r
+ // Translate IANA cipher suite ID to OpenSSL name.\r
+ //\r
+ return &TlsCipherMappingTable[Middle];\r
+ }\r
+\r
+ if (CipherId < TlsCipherMappingTable[Middle].IanaCipher) {\r
+ Right = Middle - 1;\r
+ } else {\r
+ Left = Middle + 1;\r
}\r
}\r
\r
This function sets the ciphers for use by a specified TLS object.\r
\r
@param[in] Tls Pointer to a TLS object.\r
- @param[in] CipherId Pointer to a UINT16 cipher Id.\r
+ @param[in] CipherId Array of UINT16 cipher identifiers. Each UINT16\r
+ cipher identifier comes from the TLS Cipher Suite\r
+ Registry of the IANA, interpreting Byte1 and Byte2\r
+ in network (big endian) byte order.\r
@param[in] CipherNum The number of cipher in the list.\r
\r
@retval EFI_SUCCESS The ciphers list was set successfully.\r
@retval EFI_INVALID_PARAMETER The parameter is invalid.\r
- @retval EFI_UNSUPPORTED Unsupported TLS cipher in the list.\r
+ @retval EFI_UNSUPPORTED No supported TLS cipher was found in CipherId.\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
\r
**/\r
EFI_STATUS\r
IN UINTN CipherNum\r
)\r
{\r
- TLS_CONNECTION *TlsConn;\r
- UINTN Index;\r
- CONST CHAR8 *MappingName;\r
- CHAR8 CipherString[500];\r
+ TLS_CONNECTION *TlsConn;\r
+ EFI_STATUS Status;\r
+ CONST TLS_CIPHER_MAPPING **MappedCipher;\r
+ UINTN MappedCipherBytes;\r
+ UINTN MappedCipherCount;\r
+ UINTN CipherStringSize;\r
+ UINTN Index;\r
+ CONST TLS_CIPHER_MAPPING *Mapping;\r
+ CHAR8 *CipherString;\r
+ CHAR8 *CipherStringPosition;\r
\r
TlsConn = (TLS_CONNECTION *) Tls;\r
if (TlsConn == NULL || TlsConn->Ssl == NULL || CipherId == NULL) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- MappingName = NULL;\r
-\r
- memset (CipherString, 0, sizeof (CipherString));\r
+ //\r
+ // Allocate the MappedCipher array for recording the mappings that we find\r
+ // for the input IANA identifiers in CipherId.\r
+ //\r
+ Status = SafeUintnMult (CipherNum, sizeof (*MappedCipher),\r
+ &MappedCipherBytes);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ MappedCipher = AllocatePool (MappedCipherBytes);\r
+ if (MappedCipher == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
+ //\r
+ // Map the cipher IDs, and count the number of bytes for the full\r
+ // CipherString.\r
+ //\r
+ MappedCipherCount = 0;\r
+ CipherStringSize = 0;\r
for (Index = 0; Index < CipherNum; Index++) {\r
//\r
- // Handling OpenSSL / RFC Cipher name mapping.\r
+ // Look up the IANA-to-OpenSSL mapping.\r
//\r
- MappingName = TlsGetCipherString (*(CipherId + Index));\r
- if (MappingName == NULL) {\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- if (Index != 0) {\r
+ Mapping = TlsGetCipherMapping (CipherId[Index]);\r
+ if (Mapping == NULL) {\r
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: skipping CipherId=0x%04x\n",\r
+ gEfiCallerBaseName, __FUNCTION__, CipherId[Index]));\r
//\r
- // The ciphers were separated by a colon.\r
+ // Skipping the cipher is valid because CipherId is an ordered\r
+ // preference list of ciphers, thus we can filter it as long as we\r
+ // don't change the relative order of elements on it.\r
//\r
- AsciiStrCatS (CipherString, sizeof (CipherString), ":");\r
+ continue;\r
+ }\r
+ //\r
+ // Accumulate Mapping->OpensslCipherLength into CipherStringSize. If this\r
+ // is not the first successful mapping, account for a colon (":") prefix\r
+ // too.\r
+ //\r
+ if (MappedCipherCount > 0) {\r
+ Status = SafeUintnAdd (CipherStringSize, 1, &CipherStringSize);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
+ }\r
+ }\r
+ Status = SafeUintnAdd (CipherStringSize, Mapping->OpensslCipherLength,\r
+ &CipherStringSize);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
}\r
+ //\r
+ // Record the mapping.\r
+ //\r
+ MappedCipher[MappedCipherCount++] = Mapping;\r
+ }\r
\r
- AsciiStrCatS (CipherString, sizeof (CipherString), MappingName);\r
+ //\r
+ // Verify that at least one IANA cipher ID could be mapped; account for the\r
+ // terminating NUL character in CipherStringSize; allocate CipherString.\r
+ //\r
+ if (MappedCipherCount == 0) {\r
+ DEBUG ((DEBUG_ERROR, "%a:%a: no CipherId could be mapped\n",\r
+ gEfiCallerBaseName, __FUNCTION__));\r
+ Status = EFI_UNSUPPORTED;\r
+ goto FreeMappedCipher;\r
+ }\r
+ Status = SafeUintnAdd (CipherStringSize, 1, &CipherStringSize);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
+ }\r
+ CipherString = AllocatePool (CipherStringSize);\r
+ if (CipherString == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto FreeMappedCipher;\r
}\r
\r
- AsciiStrCatS (CipherString, sizeof (CipherString), ":@STRENGTH");\r
+ //\r
+ // Go over the collected mappings and populate CipherString.\r
+ //\r
+ CipherStringPosition = CipherString;\r
+ for (Index = 0; Index < MappedCipherCount; Index++) {\r
+ Mapping = MappedCipher[Index];\r
+ //\r
+ // Append the colon (":") prefix except for the first mapping, then append\r
+ // Mapping->OpensslCipher.\r
+ //\r
+ if (Index > 0) {\r
+ *(CipherStringPosition++) = ':';\r
+ }\r
+ CopyMem (CipherStringPosition, Mapping->OpensslCipher,\r
+ Mapping->OpensslCipherLength);\r
+ CipherStringPosition += Mapping->OpensslCipherLength;\r
+ }\r
+\r
+ //\r
+ // NUL-terminate CipherString.\r
+ //\r
+ *(CipherStringPosition++) = '\0';\r
+ ASSERT (CipherStringPosition == CipherString + CipherStringSize);\r
+\r
+ //\r
+ // Log CipherString for debugging. CipherString can be very long if the\r
+ // caller provided a large CipherId array, so log CipherString in segments of\r
+ // 79 non-newline characters. (MAX_DEBUG_MESSAGE_LENGTH is usually 0x100 in\r
+ // DebugLib instances.)\r
+ //\r
+ DEBUG_CODE (\r
+ UINTN FullLength;\r
+ UINTN SegmentLength;\r
+\r
+ FullLength = CipherStringSize - 1;\r
+ DEBUG ((DEBUG_VERBOSE, "%a:%a: CipherString={\n", gEfiCallerBaseName,\r
+ __FUNCTION__));\r
+ for (CipherStringPosition = CipherString;\r
+ CipherStringPosition < CipherString + FullLength;\r
+ CipherStringPosition += SegmentLength) {\r
+ SegmentLength = FullLength - (CipherStringPosition - CipherString);\r
+ if (SegmentLength > 79) {\r
+ SegmentLength = 79;\r
+ }\r
+ DEBUG ((DEBUG_VERBOSE, "%.*a\n", SegmentLength, CipherStringPosition));\r
+ }\r
+ DEBUG ((DEBUG_VERBOSE, "}\n"));\r
+ //\r
+ // Restore the pre-debug value of CipherStringPosition by skipping over the\r
+ // trailing NUL.\r
+ //\r
+ CipherStringPosition++;\r
+ ASSERT (CipherStringPosition == CipherString + CipherStringSize);\r
+ );\r
\r
//\r
// Sets the ciphers for use by the Tls object.\r
//\r
if (SSL_set_cipher_list (TlsConn->Ssl, CipherString) <= 0) {\r
- return EFI_UNSUPPORTED;\r
+ Status = EFI_UNSUPPORTED;\r
+ goto FreeCipherString;\r
}\r
\r
- return EFI_SUCCESS;\r
+ Status = EFI_SUCCESS;\r
+\r
+FreeCipherString:\r
+ FreePool (CipherString);\r
+\r
+FreeMappedCipher:\r
+ FreePool (MappedCipher);\r
+\r
+ return Status;\r
}\r
\r
/**\r