Index: trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 39876)
+++ trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 39878)
@@ -16214,5 +16214,6 @@
       </desc>
       <param name="path" type="wstring" dir="in">
-        <desc>The path of the extension pack tarball.</desc>
+        <desc>The path of the extension pack tarball.  This can optionally be
+        followed by a "::SHA-256=hex-digit" of the tarball. </desc>
       </param>
       <param name="file" type="IExtPackFile" dir="return">
Index: trunk/src/VBox/Main/include/ExtPackManagerImpl.h
===================================================================
--- trunk/src/VBox/Main/include/ExtPackManagerImpl.h	(revision 39876)
+++ trunk/src/VBox/Main/include/ExtPackManagerImpl.h	(revision 39878)
@@ -44,5 +44,5 @@
     HRESULT     FinalConstruct();
     void        FinalRelease();
-    HRESULT     initWithFile(const char *a_pszFile, class ExtPackManager *a_pExtPackMgr, VirtualBox *a_pVirtualBox);
+    HRESULT     initWithFile(const char *a_pszFile, const char *a_pszDigest, class ExtPackManager *a_pExtPackMgr, VirtualBox *a_pVirtualBox);
     void        uninit();
     RTMEMEF_NEW_AND_DELETE_OPERATORS();
Index: trunk/src/VBox/Main/include/ExtPackUtil.h
===================================================================
--- trunk/src/VBox/Main/include/ExtPackUtil.h	(revision 39876)
+++ trunk/src/VBox/Main/include/ExtPackUtil.h	(revision 39878)
@@ -127,7 +127,9 @@
 
 int                 VBoxExtPackValidateMember(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj, char *pszError, size_t cbError);
-int                 VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss);
-int                 VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
-                                               char *pszError, size_t cbError, PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile);
+int                 VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss, PRTMANIFEST phFileManifest);
+int                 VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName,
+                                               const char *pszTarball, const char *pszTarballDigest,
+                                               char *pszError, size_t cbError,
+                                               PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile, RTCString *pStrDigest);
 
 
Index: trunk/src/VBox/Main/src-all/ExtPackManagerImpl.cpp
===================================================================
--- trunk/src/VBox/Main/src-all/ExtPackManagerImpl.cpp	(revision 39876)
+++ trunk/src/VBox/Main/src-all/ExtPackManagerImpl.cpp	(revision 39878)
@@ -89,4 +89,6 @@
     /** The path to the tarball. */
     Utf8Str             strExtPackFile;
+    /** The SHA-256 hash of the file (as string). */
+    Utf8Str             strDigest;
     /** The file handle of the extension pack file. */
     RTFILE              hExtPackFile;
@@ -220,8 +222,9 @@
  * @returns COM status code.
  * @param   a_pszFile       The path to the extension pack file.
+ * @param   a_pszDigest     The SHA-256 digest of the file. Or an empty string.
  * @param   a_pExtPackMgr   Pointer to the extension pack manager.
  * @param   a_pVirtualBox   Pointer to the VirtualBox object.
  */
-HRESULT ExtPackFile::initWithFile(const char *a_pszFile, ExtPackManager *a_pExtPackMgr, VirtualBox *a_pVirtualBox)
+HRESULT ExtPackFile::initWithFile(const char *a_pszFile, const char *a_pszDigest, ExtPackManager *a_pExtPackMgr, VirtualBox *a_pVirtualBox)
 {
     AutoInitSpan autoInitSpan(this);
@@ -237,4 +240,5 @@
     m->strWhyUnusable               = tr("ExtPack::init failed");
     m->strExtPackFile               = a_pszFile;
+    m->strDigest                    = a_pszDigest;
     m->hExtPackFile                 = NIL_RTFILE;
     m->hOurManifest                 = NIL_RTMANIFEST;
@@ -276,6 +280,6 @@
     char        szError[8192];
     RTVFSFILE   hXmlFile;
-    vrc = VBoxExtPackValidateTarball(m->hExtPackFile, NULL /*pszExtPackName*/, a_pszFile,
-                                     szError, sizeof(szError), &m->hOurManifest, &hXmlFile);
+    vrc = VBoxExtPackValidateTarball(m->hExtPackFile, NULL /*pszExtPackName*/, a_pszFile, a_pszDigest,
+                                     szError, sizeof(szError), &m->hOurManifest, &hXmlFile, &m->strDigest);
     if (RT_FAILURE(vrc))
         return initFailed(tr("%s"), szError);
@@ -549,5 +553,5 @@
                 RTVFSFSSTREAM   hTarFss;
                 char            szError[8192];
-                int vrc = VBoxExtPackOpenTarFss(m->hExtPackFile, szError, sizeof(szError), &hTarFss);
+                int vrc = VBoxExtPackOpenTarFss(m->hExtPackFile, szError, sizeof(szError), &hTarFss, NULL);
                 if (RT_SUCCESS(vrc))
                 {
@@ -2025,15 +2029,32 @@
 }
 
-STDMETHODIMP ExtPackManager::OpenExtPackFile(IN_BSTR a_bstrTarball, IExtPackFile **a_ppExtPackFile)
-{
-    CheckComArgNotNull(a_bstrTarball);
+STDMETHODIMP ExtPackManager::OpenExtPackFile(IN_BSTR a_bstrTarballAndDigest, IExtPackFile **a_ppExtPackFile)
+{
+    CheckComArgNotNull(a_bstrTarballAndDigest);
     CheckComArgOutPointerValid(a_ppExtPackFile);
-    Utf8Str strTarball(a_bstrTarball);
     AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
+
+    /* The API can optionally take a ::SHA-256=<hex-digest> attribute at the
+       end of the file name.  This is just a temporary measure for
+       backporting, in 4.2 we'll add another parameter to the method. */
+    Utf8Str strTarball;
+    Utf8Str strDigest;
+    Utf8Str strTarballAndDigest(a_bstrTarballAndDigest);
+    size_t offSha256 = Utf8Str::npos;
+    const char *pszFilename = RTPathFilename(strTarballAndDigest.c_str());
+    if (pszFilename)
+        offSha256 = strTarballAndDigest.find("::SHA-256=", pszFilename - strTarballAndDigest.c_str());
+    if (offSha256 == Utf8Str::npos)
+        strTarball = strTarballAndDigest;
+    else
+    {
+        strTarball = strTarballAndDigest.substr(0, offSha256);
+        strDigest  = strTarballAndDigest.substr(offSha256 + sizeof("::SHA-256=") - 1);
+    }
 
     ComObjPtr<ExtPackFile> NewExtPackFile;
     HRESULT hrc = NewExtPackFile.createObject();
     if (SUCCEEDED(hrc))
-        hrc = NewExtPackFile->initWithFile(strTarball.c_str(), this, m->pVirtualBox);
+        hrc = NewExtPackFile->initWithFile(strTarball.c_str(), strDigest.c_str(), this, m->pVirtualBox);
     if (SUCCEEDED(hrc))
         NewExtPackFile.queryInterfaceTo(a_ppExtPackFile);
@@ -2599,6 +2620,7 @@
 {
     AssertReturn(m->enmContext == VBOXEXTPACKCTX_PER_USER_DAEMON, E_UNEXPECTED);
-    RTCString const * const pStrName     = &a_pExtPackFile->m->Desc.strName;
-    RTCString const * const pStrTarball  = &a_pExtPackFile->m->strExtPackFile;
+    RTCString const * const pStrName          = &a_pExtPackFile->m->Desc.strName;
+    RTCString const * const pStrTarball       = &a_pExtPackFile->m->strExtPackFile;
+    RTCString const * const pStrTarballDigest = &a_pExtPackFile->m->strDigest;
 
     AutoCaller autoCaller(this);
@@ -2631,5 +2653,4 @@
              * even on failure, to be on the safe side).
              */
-            /** @todo add a hash (SHA-256) of the tarball or maybe just the manifest. */
             hrc = runSetUidToRootHelper(a_pstrDisplayInfo,
                                         "install",
@@ -2638,4 +2659,5 @@
                                         "--name",       pStrName->c_str(),
                                         "--tarball",    pStrTarball->c_str(),
+                                        "--sha-256",    pStrTarballDigest->c_str(),
                                         pExtPack ? "--replace" : (const char *)NULL,
                                         (const char *)NULL);
Index: trunk/src/VBox/Main/src-all/ExtPackUtil.cpp
===================================================================
--- trunk/src/VBox/Main/src-all/ExtPackUtil.cpp	(revision 39876)
+++ trunk/src/VBox/Main/src-all/ExtPackUtil.cpp	(revision 39878)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2010-2011 Oracle Corporation
+ * Copyright (C) 2010-2012 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -28,4 +28,5 @@
 #include <iprt/param.h>
 #include <iprt/path.h>
+#include <iprt/sha.h>
 #include <iprt/string.h>
 #include <iprt/vfs.h>
@@ -784,4 +785,77 @@
     return rc;
 }
+
+
+/**
+ * Verifies the file digest (if specified) and returns the SHA-256 of the file.
+ *
+ * @returns
+ * @param   hFileManifest       Manifest containing a SHA-256 digest of the file
+ *                              that was calculated as the file was processed.
+ * @param   pszFileDigest       SHA-256 digest of the file.
+ * @param   pStrDigest          Where to return the SHA-256 digest. Optional.
+ * @param   pszError            Where to write an error message on failure.
+ * @param   cbError             The size of the @a pszError buffer.
+ */
+static int vboxExtPackVerifyFileDigest(RTMANIFEST hFileManifest, const char *pszFileDigest,
+                                       RTCString *pStrDigest, char *pszError, size_t cbError)
+{
+    /*
+     * Extract the SHA-256 entry for the extpack file.
+     */
+    char szCalculatedDigest[RTSHA256_DIGEST_LEN + 1];
+    int rc = RTManifestEntryQueryAttr(hFileManifest, "extpack", NULL /*no name*/, RTMANIFEST_ATTR_SHA256,
+                                      szCalculatedDigest, sizeof(szCalculatedDigest), NULL);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Convert the two strings to binary form before comparing.
+         * We convert the calculated hash even if we don't have anything to
+         * compare with, just to validate it.
+         */
+        uint8_t abCalculatedHash[RTSHA256_HASH_SIZE];
+        rc = RTSha256FromString(szCalculatedDigest, abCalculatedHash);
+        if (RT_SUCCESS(rc))
+        {
+            if (   pszFileDigest
+                && *pszFileDigest != '\0')
+            {
+                uint8_t abFileHash[RTSHA256_HASH_SIZE];
+                rc = RTSha256FromString(pszFileDigest, abFileHash);
+                if (RT_SUCCESS(rc))
+                {
+                    if (memcmp(abFileHash, abCalculatedHash, sizeof(abFileHash)))
+                    {
+                        vboxExtPackSetError(pszError, cbError, "The extension pack file has changed (SHA-256 mismatch)");
+                        rc = VERR_NOT_EQUAL;
+                    }
+                }
+                else
+                    vboxExtPackSetError(pszError, cbError, "Bad SHA-256 '%s': %Rrc", szCalculatedDigest, rc);
+            }
+
+            /*
+             * Set the output hash on success.
+             */
+            if (pStrDigest && RT_SUCCESS(rc))
+            {
+                try
+                {
+                    *pStrDigest = szCalculatedDigest;
+                }
+                catch (std::bad_alloc)
+                {
+                    rc = VERR_NO_MEMORY;
+                }
+            }
+        }
+        else
+            vboxExtPackSetError(pszError, cbError, "Bad SHA-256 '%s': %Rrc", szCalculatedDigest, rc);
+    }
+    else
+        vboxExtPackSetError(pszError, cbError, "RTManifestEntryGetAttr: %Rrc", rc);
+    return rc;
+}
+
 
 
@@ -1044,6 +1118,10 @@
  * @param   cbError             The size of the buffer @a pszError points to.
  * @param   phTarFss            Where to return the filesystem stream handle.
- */
-int VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss)
+ * @param   phFileManifest      Where to return a manifest where the tarball is
+ *                              gettting hashed.  The entry will be called
+ *                              "extpack" and be ready when the file system
+ *                              stream is at an end.  Optional.
+ */
+int VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss, PRTMANIFEST phFileManifest)
 {
     Assert(cbError > 0);
@@ -1064,22 +1142,45 @@
         return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsIoStrmFromRTFile failed: %Rrc", rc);
 
-    RTVFSIOSTREAM hGunzipIos;
-    rc = RTZipGzipDecompressIoStream(hTarballIos, 0 /*fFlags*/, &hGunzipIos);
+    RTMANIFEST hFileManifest = NIL_RTMANIFEST;
+    rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest);
     if (RT_SUCCESS(rc))
     {
-        RTVFSFSSTREAM hTarFss;
-        rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
+        RTVFSIOSTREAM hPtIos;
+        rc = RTManifestEntryAddPassthruIoStream(hFileManifest, hTarballIos, "extpack", RTMANIFEST_ATTR_SHA256, true /*read*/, &hPtIos);
         if (RT_SUCCESS(rc))
         {
-            RTVfsIoStrmRelease(hGunzipIos);
-            RTVfsIoStrmRelease(hTarballIos);
-            *phTarFss = hTarFss;
-            return VINF_SUCCESS;
-        }
-        vboxExtPackSetError(pszError, cbError, "RTZipTarFsStreamFromIoStream failed: %Rrc", rc);
-        RTVfsIoStrmRelease(hGunzipIos);
+            RTVFSIOSTREAM hGunzipIos;
+            rc = RTZipGzipDecompressIoStream(hPtIos, 0 /*fFlags*/, &hGunzipIos);
+            if (RT_SUCCESS(rc))
+            {
+                RTVFSFSSTREAM hTarFss;
+                rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
+                if (RT_SUCCESS(rc))
+                {
+                    RTVfsIoStrmRelease(hPtIos);
+                    RTVfsIoStrmRelease(hGunzipIos);
+                    RTVfsIoStrmRelease(hTarballIos);
+                    *phTarFss = hTarFss;
+                    if (phFileManifest)
+                        *phFileManifest = hFileManifest;
+                    else
+                        RTManifestRelease(hFileManifest);
+                    return VINF_SUCCESS;
+                }
+
+                vboxExtPackSetError(pszError, cbError, "RTZipTarFsStreamFromIoStream failed: %Rrc", rc);
+                RTVfsIoStrmRelease(hGunzipIos);
+            }
+            else
+                vboxExtPackSetError(pszError, cbError, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
+            RTVfsIoStrmRelease(hPtIos);
+        }
+        else
+            vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddPassthruIoStream failed: %Rrc", rc);
+        RTManifestRelease(hFileManifest);
     }
     else
-        vboxExtPackSetError(pszError, cbError, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
+        vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
+
     RTVfsIoStrmRelease(hTarballIos);
     return rc;
@@ -1102,4 +1203,6 @@
  * @param   pszTarball          The name of the tarball in case we have to
  *                              complain about something.
+ * @param   pszTarballDigest    The SHA-256 digest of the tarball.  Empty string
+ *                              if no digest available.
  * @param   pszError            Where to store an error message on failure.
  * @param   cbError             The size of the buffer @a pszError points to.
@@ -1109,9 +1212,11 @@
  * @param   phXmlFile           Where to optionally return the memorized XML
  *                              file.
- *
- * @todo    This function is a bit too long and should be split up if possible.
- */
-int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
-                               char *pszError, size_t cbError, PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile)
+ * @param   pStrDigest          Where to return the digest of the file.
+ *                              Optional.
+ */
+int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName,
+                               const char *pszTarball, const char *pszTarballDigest,
+                               char *pszError, size_t cbError,
+                               PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile, RTCString *pStrDigest)
 {
     /*
@@ -1129,6 +1234,7 @@
      * Open the tar.gz filesystem stream and set up an manifest in-memory file.
      */
-    RTVFSFSSTREAM hTarFss;
-    int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss);
+    RTMANIFEST      hFileManifest;
+    RTVFSFSSTREAM   hTarFss;
+    int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss, &hFileManifest);
     if (RT_FAILURE(rc))
         return rc;
@@ -1141,7 +1247,7 @@
          * Process the tarball (would be nice to move this to a function).
          */
-        RTVFSFILE hXmlFile      = NIL_RTVFSFILE;
-        RTVFSFILE hManifestFile = NIL_RTVFSFILE;
-        RTVFSFILE hSignatureFile= NIL_RTVFSFILE;
+        RTVFSFILE hXmlFile       = NIL_RTVFSFILE;
+        RTVFSFILE hManifestFile  = NIL_RTVFSFILE;
+        RTVFSFILE hSignatureFile = NIL_RTVFSFILE;
         for (;;)
         {
@@ -1210,4 +1316,14 @@
 
         /*
+         * Check the integrity of the tarball file.
+         */
+        if (RT_SUCCESS(rc))
+        {
+            RTVfsFsStrmRelease(hTarFss);
+            hTarFss = NIL_RTVFSFSSTREAM;
+            rc = vboxExtPackVerifyFileDigest(hFileManifest, pszTarballDigest, pStrDigest, pszError, cbError);
+        }
+
+        /*
          * If we've successfully processed the tarball, verify that the
          * mandatory files are present.
@@ -1263,4 +1379,5 @@
         vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
     RTVfsFsStrmRelease(hTarFss);
+    RTManifestRelease(hFileManifest);
 
     return rc;
Index: trunk/src/VBox/Main/src-helper-apps/VBoxExtPackHelperApp.cpp
===================================================================
--- trunk/src/VBox/Main/src-helper-apps/VBoxExtPackHelperApp.cpp	(revision 39876)
+++ trunk/src/VBox/Main/src-helper-apps/VBoxExtPackHelperApp.cpp	(revision 39878)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2010-2011 Oracle Corporation
+ * Copyright (C) 2010-2012 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -34,4 +34,5 @@
 #include <iprt/path.h>
 #include <iprt/process.h>
+#include <iprt/sha.h>
 #include <iprt/string.h>
 #include <iprt/stream.h>
@@ -254,5 +255,5 @@
 {
     char szError[8192];
-    int rc = VBoxExtPackOpenTarFss(hTarballFile, szError, sizeof(szError), phTarFss);
+    int rc = VBoxExtPackOpenTarFss(hTarballFile, szError, sizeof(szError), phTarFss, NULL);
     if (RT_FAILURE(rc))
     {
@@ -597,4 +598,5 @@
  * @param   pszTarball          The name of the tarball in case we have to
  *                              complain about something.
+ * @param   pszTarballDigest    The SHA-256 digest of the tarball.
  * @param   phValidManifest     Where to return the handle to fully validated
  *                              the manifest for the extension pack.  This
@@ -602,12 +604,13 @@
  */
 static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
-                                         PRTMANIFEST phValidManifest)
+                                         const char *pszTarballDigest, PRTMANIFEST phValidManifest)
 {
     *phValidManifest = NIL_RTMANIFEST;
     RTMsgInfo("Validating extension pack '%s' ('%s')...", pszTarball, pszExtPackName);
+    Assert(pszTarballDigest && *pszTarballDigest);
 
     char szError[8192];
-    int rc = VBoxExtPackValidateTarball(hTarballFile, pszExtPackName, pszTarball,
-                                        szError, sizeof(szError), phValidManifest, NULL /*phXmlFile*/);
+    int rc = VBoxExtPackValidateTarball(hTarballFile, pszExtPackName, pszTarball, pszTarballDigest,
+                                        szError, sizeof(szError), phValidManifest, NULL /*phXmlFile*/, NULL /*pStrDigest*/);
     if (RT_FAILURE(rc))
     {
@@ -627,4 +630,6 @@
  * @param   pszCertDir          The certificat directory.
  * @param   pszTarball          The tarball name.
+ * @param   pszTarballDigest    The SHA-256 digest of the tarball.  Empty string
+ *                              if no digest available.
  * @param   hTarballFile        The handle to open the @a pszTarball file.
  * @param   hTarballFileOpt     The tarball file handle (optional).
@@ -634,5 +639,5 @@
  */
 static RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
-                             RTFILE hTarballFile, RTFILE hTarballFileOpt,
+                             const char *pszTarballDigest, RTFILE hTarballFile, RTFILE hTarballFileOpt,
                              const char *pszName, const char *pszMangledName, bool fReplace)
 {
@@ -719,5 +724,5 @@
 
     RTMANIFEST hValidManifest = NIL_RTMANIFEST;
-    RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszName, pszTarball, &hValidManifest);
+    RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszName, pszTarball, pszTarballDigest, &hValidManifest);
     if (rcExit == RTEXITCODE_SUCCESS)
         rcExit = UnpackExtPack(hTarballFile, szTmpPath, hValidManifest, pszTarball);
@@ -783,5 +788,6 @@
         { "--tarball",      't',   RTGETOPT_REQ_STRING  },
         { "--tarball-fd",   'd',   RTGETOPT_REQ_UINT64  },
-        { "--replace",      'r',   RTGETOPT_REQ_NOTHING }
+        { "--replace",      'r',   RTGETOPT_REQ_NOTHING },
+        { "--sha-256",      's',   RTGETOPT_REQ_STRING  }
     };
     RTGETOPTSTATE   GetState;
@@ -790,10 +796,11 @@
         return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
 
-    const char     *pszBaseDir      = NULL;
-    const char     *pszCertDir      = NULL;
-    const char     *pszName         = NULL;
-    const char     *pszTarball      = NULL;
-    RTFILE          hTarballFileOpt = NIL_RTFILE;
-    bool            fReplace        = false;
+    const char     *pszBaseDir          = NULL;
+    const char     *pszCertDir          = NULL;
+    const char     *pszName             = NULL;
+    const char     *pszTarball          = NULL;
+    const char     *pszTarballDigest    = NULL;
+    RTFILE          hTarballFileOpt     = NIL_RTFILE;
+    bool            fReplace            = false;
     RTGETOPTUNION   ValueUnion;
     int             ch;
@@ -849,4 +856,17 @@
                 break;
 
+            case 's':
+            {
+                if (pszTarballDigest)
+                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --sha-256 options");
+                pszTarballDigest = ValueUnion.psz;
+
+                uint8_t abDigest[RTSHA256_HASH_SIZE];
+                rc = RTSha256FromString(pszTarballDigest, abDigest);
+                if (RT_FAILURE(rc))
+                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Bad SHA-256 string: %Rrc", rc);
+                break;
+            }
+
             case 'h':
             case 'V':
@@ -865,4 +885,6 @@
     if (!pszTarball)
         return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --tarball option");
+    if (!pszTarballDigest)
+        return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --sha-256 option");
 
     /*
@@ -878,5 +900,5 @@
     if (RT_SUCCESS(rc))
     {
-        rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, hTarballFile, hTarballFileOpt,
+        rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, pszTarballDigest, hTarballFile, hTarballFileOpt,
                             pszName, pstrMangledName->c_str(), fReplace);
         RTFileClose(hTarballFile);
