Index: /trunk/include/iprt/crypto/shacrypt.h
===================================================================
--- /trunk/include/iprt/crypto/shacrypt.h	(revision 102374)
+++ /trunk/include/iprt/crypto/shacrypt.h	(revision 102375)
@@ -77,5 +77,11 @@
  * @param   pszSalt             Salt to use.
  *                              Must be >= RT_SHACRYPT_MIN_SALT_LEN and <= RT_SHACRYPT_MAX_SALT_LEN.
- * @param   cRounds             Number of rounds to use.  @sa RT_SHACRYPT_DEFAULT_ROUNDS
+ *
+ *                              This parameter also accepts crypted password strings produced by RTCrShaCrypt256ToString().
+ *
+ *                              This approach is used by many *crypt implementations -- it allows feeding the user-provided
+ *                              password and the crypted password from "the password file" to the function. If it returns the
+ *                              same crypted password then the user-provided password must be the correct one.
+ * @param   cRounds             Number of rounds to use. @sa RT_SHACRYPT_DEFAULT_ROUNDS
  * @param   abHash              Where to return the hash on success.
  *
@@ -108,4 +114,10 @@
  * @param   pszSalt             Salt to use.
  *                              Must be >= RT_SHACRYPT_MIN_SALT_LEN and <= RT_SHACRYPT_MAX_SALT_LEN.
+ *
+ *                              This parameter also accepts crypted password strings produced by RTCrShaCrypt512ToString().
+ *
+ *                              This approach is used by many *crypt implementations -- it allows feeding the user-provided
+ *                              password and the crypted password from "the password file" to the function. If it returns the
+ *                              same crypted password then the user-provided password must be the correct one.
  * @param   cRounds             Number of rounds to use. @sa RT_SHACRYPT_DEFAULT_ROUNDS
  * @param   abHash              Where to return the hash on success.
Index: /trunk/src/VBox/Runtime/common/crypto/shacrypt.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/crypto/shacrypt.cpp	(revision 102374)
+++ /trunk/src/VBox/Runtime/common/crypto/shacrypt.cpp	(revision 102375)
@@ -48,4 +48,9 @@
 
 
+/** The digest prefix for SHAcrypt256 strings. */
+#define RT_SHACRYPT_DIGEST_PREFIX_256_STR       "$5$"
+/** The digest prefix for SHAcrypt512 strings. */
+#define RT_SHACRYPT_DIGEST_PREFIX_512_STR       "$6$"
+
 
 RTR3DECL(int) RTCrShaCryptGenerateSalt(char *pszSalt, size_t cchSalt)
@@ -63,4 +68,43 @@
 
 
+/**
+ * Extracts the salt from a given string.
+ *
+ * @returns Pointer to the salt string, or NULL if not found / invalid.
+ * @param   pszStr              String to extract salt from.
+ * @param   pcchSalt            Where to reutrn the extracted salt length (in characters).
+ */
+static const char *rtCrShaCryptExtractSalt(const char *pszStr, size_t *pcchSalt)
+{
+    size_t cchSalt = strlen(pszStr);
+
+    /* Searches for a known SHAcrypt string prefix and skips it. */
+#define BEGINS_WITH(a_szMatch) \
+        (cchSalt >= sizeof(a_szMatch) - 1U && memcmp(pszStr, a_szMatch, sizeof(a_szMatch) - 1U) == 0)
+    if (BEGINS_WITH(RT_SHACRYPT_DIGEST_PREFIX_256_STR))
+    {
+        cchSalt -= sizeof(RT_SHACRYPT_DIGEST_PREFIX_256_STR) - 1;
+        pszStr  += sizeof(RT_SHACRYPT_DIGEST_PREFIX_256_STR) - 1;
+    }
+    else if (BEGINS_WITH(RT_SHACRYPT_DIGEST_PREFIX_512_STR))
+    {
+        cchSalt -= sizeof(RT_SHACRYPT_DIGEST_PREFIX_512_STR) - 1;
+        pszStr  += sizeof(RT_SHACRYPT_DIGEST_PREFIX_512_STR) - 1;
+    }
+#undef BEGINS_WITH
+
+    /* Search for the end of the salt string, denoted by a '$'. */
+    size_t cchLen = 0;
+    while (   cchLen < cchSalt
+           && pszStr[cchLen] != '$')
+        cchLen++;
+
+    AssertMsgReturn(cchLen >= RT_SHACRYPT_MIN_SALT_LEN && cchLen <= RT_SHACRYPT_MAX_SALT_LEN, ("len=%zu\n", cchLen), NULL);
+    *pcchSalt = cchLen;
+
+    return pszStr;
+}
+
+
 RTR3DECL(int) RTCrShaCrypt256(const char *pszKey, const char *pszSalt, uint32_t cRounds, uint8_t abHash[RTSHA256_HASH_SIZE])
 {
@@ -71,7 +115,8 @@
     size_t const cchKey     = strlen(pszKey);
     AssertReturn(cchKey, VERR_INVALID_PARAMETER);
-    size_t const cchSalt    = strlen(pszSalt);
-    AssertMsgReturn(cchSalt >= RT_SHACRYPT_MIN_SALT_LEN && cchSalt <= RT_SHACRYPT_MAX_SALT_LEN, ("len=%zu\n", cchSalt),
-                    VERR_INVALID_PARAMETER);
+
+    size_t cchSalt;
+    pszSalt = rtCrShaCryptExtractSalt(pszSalt, &cchSalt);
+    AssertPtrReturn(pszSalt, VERR_INVALID_PARAMETER);
 
     uint8_t abDigest[RTSHA256_HASH_SIZE];
@@ -202,7 +247,7 @@
     size_t cchPrefix;
     if (cRounds == RT_SHACRYPT_DEFAULT_ROUNDS)
-        cchPrefix = RTStrPrintf2(psz, cchString, "$5$%s$", pszSalt);
+        cchPrefix = RTStrPrintf2(psz, cchString, "%s%s$", RT_SHACRYPT_DIGEST_PREFIX_256_STR, pszSalt);
     else
-        cchPrefix = RTStrPrintf2(psz, cchString, "$5$rounds=%RU32$%s$", cRounds, pszSalt);
+        cchPrefix = RTStrPrintf2(psz, cchString, "%srounds=%RU32$%s$", RT_SHACRYPT_DIGEST_PREFIX_256_STR, cRounds, pszSalt);
     AssertReturn(cchPrefix > 0, VERR_BUFFER_OVERFLOW);
     AssertReturn(cch >= cchPrefix, VERR_BUFFER_OVERFLOW);
@@ -255,9 +300,10 @@
     AssertReturn   (cRounds, VERR_INVALID_PARAMETER);
 
-    size_t const cchKey     = strlen(pszKey);
+    size_t const cchKey = strlen(pszKey);
     AssertReturn(cchKey, VERR_INVALID_PARAMETER);
-    size_t const cchSalt    = strlen(pszSalt);
-    AssertMsgReturn(cchSalt >= RT_SHACRYPT_MIN_SALT_LEN && cchSalt <= RT_SHACRYPT_MAX_SALT_LEN, ("len=%zu\n", cchSalt),
-                    VERR_INVALID_PARAMETER);
+
+    size_t cchSalt;
+    pszSalt = rtCrShaCryptExtractSalt(pszSalt, &cchSalt);
+    AssertPtrReturn(pszSalt, VERR_INVALID_PARAMETER);
 
     uint8_t abDigest[RTSHA512_HASH_SIZE];
@@ -386,7 +432,7 @@
     size_t cchPrefix;
     if (cRounds == RT_SHACRYPT_DEFAULT_ROUNDS)
-        cchPrefix = RTStrPrintf2(psz, cchString, "$6$%s$", pszSalt);
+        cchPrefix = RTStrPrintf2(psz, cchString, "%s%s$", RT_SHACRYPT_DIGEST_PREFIX_512_STR, pszSalt);
     else
-        cchPrefix = RTStrPrintf2(psz, cchString, "$6$rounds=%RU32$%s$", cRounds, pszSalt);
+        cchPrefix = RTStrPrintf2(psz, cchString, "%srounds=%RU32$%s$", RT_SHACRYPT_DIGEST_PREFIX_512_STR, cRounds, pszSalt);
     AssertReturn(cchPrefix > 0, VERR_BUFFER_OVERFLOW);
     AssertReturn(cch >= cchPrefix, VERR_BUFFER_OVERFLOW);
Index: /trunk/src/VBox/Runtime/testcase/tstRTShaCrypt.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstRTShaCrypt.cpp	(revision 102374)
+++ /trunk/src/VBox/Runtime/testcase/tstRTShaCrypt.cpp	(revision 102375)
@@ -247,4 +247,37 @@
                 if (RTStrCmp(szResult, g_aTests[i].pszResultStr))
                     RTTestIFailed("#%u: Returns '%s', expected '%s'", i, szResult, g_aTests[i].pszResultStr);
+
+                /* Now do the same, but hand-in the result string as the salt.
+                 *
+                 * This approach is used by many *crypt implementations -- it allows feeding the user-provided password and the
+                 * crypted password from "the password file" to the function. If it returns the same crypted password then the
+                 * user-provided password must be the correct one.
+                 */
+                switch (enmType)
+                {
+                    case TST_DIGESTTYPE_SHA256:
+                    {
+                        rc = RTCrShaCrypt256(g_aTests[i].pszPassword, g_aTests[i].pszResultStr, cRounds, abDigest);
+                        if (RT_SUCCESS(rc))
+                            rc = RTCrShaCrypt256ToString(abDigest, pszSalt, cRounds, szResult, sizeof(szResult));
+                        break;
+                    }
+
+                    case TST_DIGESTTYPE_SHA512:
+                    {
+                        rc = RTCrShaCrypt512(g_aTests[i].pszPassword, g_aTests[i].pszResultStr, cRounds, abDigest);
+                        if (RT_SUCCESS(rc))
+                            rc = RTCrShaCrypt512ToString(abDigest, pszSalt, cRounds, szResult, sizeof(szResult));
+                        break;
+                    }
+
+                    default:
+                        AssertFailed();
+                        break;
+                }
+
+                if (RTStrCmp(szResult, g_aTests[i].pszResultStr))
+                    RTTestIFailed("#%u (result as hash): Returns '%s', expected '%s'",
+                                  i, szResult, g_aTests[i].pszResultStr);
             }
         }
