diff --git a/Makefile.am b/Makefile.am
index a8f133e99341f6b18b71d149c1b0ee416251cb7e..7704dd97fb52df01b6820605fe674f92d0a2d310 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -214,6 +214,7 @@ server_PYTHON = \
 utils_PYTHON = \
 	lib/utils/__init__.py \
 	lib/utils/algo.py \
+	lib/utils/hash.py \
 	lib/utils/log.py \
 	lib/utils/mlock.py \
 	lib/utils/retry.py \
@@ -484,6 +485,7 @@ python_tests = \
 	test/ganeti.ssh_unittest.py \
 	test/ganeti.uidpool_unittest.py \
 	test/ganeti.utils.algo_unittest.py \
+	test/ganeti.utils.hash_unittest.py \
 	test/ganeti.utils.mlock_unittest.py \
 	test/ganeti.utils.retry_unittest.py \
 	test/ganeti.utils.text_unittest.py \
diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py
index c1cec2fd0cb8e6bfb06facd54c1da5085622ddce..b8769656e82d7ff7fa3e847e8cc7ba0841e283ba 100644
--- a/lib/utils/__init__.py
+++ b/lib/utils/__init__.py
@@ -46,7 +46,6 @@ import signal
 import OpenSSL
 import datetime
 import calendar
-import hmac
 
 from cStringIO import StringIO
 
@@ -59,6 +58,7 @@ from ganeti.utils.retry import * # pylint: disable-msg=W0401
 from ganeti.utils.text import * # pylint: disable-msg=W0401
 from ganeti.utils.mlock import * # pylint: disable-msg=W0401
 from ganeti.utils.log import * # pylint: disable-msg=W0401
+from ganeti.utils.hash import * # pylint: disable-msg=W0401
 
 _locksheld = []
 
@@ -850,55 +850,6 @@ def ResetTempfileModule():
                      " '_once_lock' and '_name_sequence' attributes")
 
 
-def _FingerprintFile(filename):
-  """Compute the fingerprint of a file.
-
-  If the file does not exist, a None will be returned
-  instead.
-
-  @type filename: str
-  @param filename: the filename to checksum
-  @rtype: str
-  @return: the hex digest of the sha checksum of the contents
-      of the file
-
-  """
-  if not (os.path.exists(filename) and os.path.isfile(filename)):
-    return None
-
-  f = open(filename)
-
-  fp = compat.sha1_hash()
-  while True:
-    data = f.read(4096)
-    if not data:
-      break
-
-    fp.update(data)
-
-  return fp.hexdigest()
-
-
-def FingerprintFiles(files):
-  """Compute fingerprints for a list of files.
-
-  @type files: list
-  @param files: the list of filename to fingerprint
-  @rtype: dict
-  @return: a dictionary filename: fingerprint, holding only
-      existing files
-
-  """
-  ret = {}
-
-  for filename in files:
-    cksum = _FingerprintFile(filename)
-    if cksum:
-      ret[filename] = cksum
-
-  return ret
-
-
 def ForceDictType(target, key_types, allowed_values=None):
   """Force the values of a dict to have certain types.
 
@@ -2541,41 +2492,6 @@ def LoadSignedX509Certificate(cert_pem, key):
   return (cert, salt)
 
 
-def Sha1Hmac(key, text, salt=None):
-  """Calculates the HMAC-SHA1 digest of a text.
-
-  HMAC is defined in RFC2104.
-
-  @type key: string
-  @param key: Secret key
-  @type text: string
-
-  """
-  if salt:
-    salted_text = salt + text
-  else:
-    salted_text = text
-
-  return hmac.new(key, salted_text, compat.sha1).hexdigest()
-
-
-def VerifySha1Hmac(key, text, digest, salt=None):
-  """Verifies the HMAC-SHA1 digest of a text.
-
-  HMAC is defined in RFC2104.
-
-  @type key: string
-  @param key: Secret key
-  @type text: string
-  @type digest: string
-  @param digest: Expected digest
-  @rtype: bool
-  @return: Whether HMAC-SHA1 digest matches
-
-  """
-  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
-
-
 def FindMatch(data, name):
   """Tries to find an item in a dictionary matching a name.
 
diff --git a/lib/utils/hash.py b/lib/utils/hash.py
new file mode 100644
index 0000000000000000000000000000000000000000..2358762f27f1844b0582a2a412f8f14b5c922aa0
--- /dev/null
+++ b/lib/utils/hash.py
@@ -0,0 +1,112 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+"""Utility functions for hashing.
+
+"""
+
+import os
+import hmac
+
+from ganeti import compat
+
+
+def Sha1Hmac(key, text, salt=None):
+  """Calculates the HMAC-SHA1 digest of a text.
+
+  HMAC is defined in RFC2104.
+
+  @type key: string
+  @param key: Secret key
+  @type text: string
+
+  """
+  if salt:
+    salted_text = salt + text
+  else:
+    salted_text = text
+
+  return hmac.new(key, salted_text, compat.sha1).hexdigest()
+
+
+def VerifySha1Hmac(key, text, digest, salt=None):
+  """Verifies the HMAC-SHA1 digest of a text.
+
+  HMAC is defined in RFC2104.
+
+  @type key: string
+  @param key: Secret key
+  @type text: string
+  @type digest: string
+  @param digest: Expected digest
+  @rtype: bool
+  @return: Whether HMAC-SHA1 digest matches
+
+  """
+  return digest.lower() == Sha1Hmac(key, text, salt=salt).lower()
+
+
+def _FingerprintFile(filename):
+  """Compute the fingerprint of a file.
+
+  If the file does not exist, a None will be returned
+  instead.
+
+  @type filename: str
+  @param filename: the filename to checksum
+  @rtype: str
+  @return: the hex digest of the sha checksum of the contents
+      of the file
+
+  """
+  if not (os.path.exists(filename) and os.path.isfile(filename)):
+    return None
+
+  f = open(filename)
+
+  fp = compat.sha1_hash()
+  while True:
+    data = f.read(4096)
+    if not data:
+      break
+
+    fp.update(data)
+
+  return fp.hexdigest()
+
+
+def FingerprintFiles(files):
+  """Compute fingerprints for a list of files.
+
+  @type files: list
+  @param files: the list of filename to fingerprint
+  @rtype: dict
+  @return: a dictionary filename: fingerprint, holding only
+      existing files
+
+  """
+  ret = {}
+
+  for filename in files:
+    cksum = _FingerprintFile(filename)
+    if cksum:
+      ret[filename] = cksum
+
+  return ret
diff --git a/test/ganeti.utils.hash_unittest.py b/test/ganeti.utils.hash_unittest.py
new file mode 100755
index 0000000000000000000000000000000000000000..3095935ba59e22989fa3377ac3d76b8ab059f350
--- /dev/null
+++ b/test/ganeti.utils.hash_unittest.py
@@ -0,0 +1,113 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script for testing ganeti.utils.hash"""
+
+import unittest
+import random
+import operator
+import tempfile
+
+from ganeti import constants
+from ganeti import utils
+
+import testutils
+
+
+class TestHmacFunctions(unittest.TestCase):
+  # Digests can be checked with "openssl sha1 -hmac $key"
+  def testSha1Hmac(self):
+    self.assertEqual(utils.Sha1Hmac("", ""),
+                     "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d")
+    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", "Hello World"),
+                     "ef4f3bda82212ecb2f7ce868888a19092481f1fd")
+    self.assertEqual(utils.Sha1Hmac("TguMTA2K", ""),
+                     "f904c2476527c6d3e6609ab683c66fa0652cb1dc")
+
+    longtext = 1500 * "The quick brown fox jumps over the lazy dog\n"
+    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", longtext),
+                     "35901b9a3001a7cdcf8e0e9d7c2e79df2223af54")
+
+  def testSha1HmacSalt(self):
+    self.assertEqual(utils.Sha1Hmac("TguMTA2K", "", salt="abc0"),
+                     "4999bf342470eadb11dfcd24ca5680cf9fd7cdce")
+    self.assertEqual(utils.Sha1Hmac("TguMTA2K", "", salt="abc9"),
+                     "17a4adc34d69c0d367d4ffbef96fd41d4df7a6e8")
+    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", "Hello World", salt="xyz0"),
+                     "7f264f8114c9066afc9bb7636e1786d996d3cc0d")
+
+  def testVerifySha1Hmac(self):
+    self.assert_(utils.VerifySha1Hmac("", "", ("fbdb1d1b18aa6c08324b"
+                                               "7d64b71fb76370690e1d")))
+    self.assert_(utils.VerifySha1Hmac("TguMTA2K", "",
+                                      ("f904c2476527c6d3e660"
+                                       "9ab683c66fa0652cb1dc")))
+
+    digest = "ef4f3bda82212ecb2f7ce868888a19092481f1fd"
+    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World", digest))
+    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
+                                      digest.lower()))
+    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
+                                      digest.upper()))
+    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
+                                      digest.title()))
+
+  def testVerifySha1HmacSalt(self):
+    self.assert_(utils.VerifySha1Hmac("TguMTA2K", "",
+                                      ("17a4adc34d69c0d367d4"
+                                       "ffbef96fd41d4df7a6e8"),
+                                      salt="abc9"))
+    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
+                                      ("7f264f8114c9066afc9b"
+                                       "b7636e1786d996d3cc0d"),
+                                      salt="xyz0"))
+
+
+class TestFingerprintFiles(unittest.TestCase):
+  def setUp(self):
+    self.tmpfile = tempfile.NamedTemporaryFile()
+    self.tmpfile2 = tempfile.NamedTemporaryFile()
+    utils.WriteFile(self.tmpfile2.name, data="Hello World\n")
+    self.results = {
+      self.tmpfile.name: "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+      self.tmpfile2.name: "648a6a6ffffdaa0badb23b8baf90b6168dd16b3a",
+      }
+
+  def testSingleFile(self):
+    self.assertEqual(utils.hash._FingerprintFile(self.tmpfile.name),
+                     self.results[self.tmpfile.name])
+
+    self.assertEqual(utils.hash._FingerprintFile("/no/such/file"), None)
+
+  def testBigFile(self):
+    self.tmpfile.write("A" * 8192)
+    self.tmpfile.flush()
+    self.assertEqual(utils.hash._FingerprintFile(self.tmpfile.name),
+                     "35b6795ca20d6dc0aff8c7c110c96cd1070b8c38")
+
+  def testMultiple(self):
+    all_files = self.results.keys()
+    all_files.append("/no/such/file")
+    self.assertEqual(utils.FingerprintFiles(self.results.keys()), self.results)
+
+
+if __name__ == "__main__":
+  testutils.GanetiTestProgram()
diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py
index 2f8b00c72ec81a8f37e2ae16b9162f087cc73f67..574edf906d896d86bab4b7af55b58db51d1e7313 100755
--- a/test/ganeti.utils_unittest.py
+++ b/test/ganeti.utils_unittest.py
@@ -1471,34 +1471,6 @@ class RunInSeparateProcess(unittest.TestCase):
                       utils.RunInSeparateProcess, _exc)
 
 
-class TestFingerprintFiles(unittest.TestCase):
-  def setUp(self):
-    self.tmpfile = tempfile.NamedTemporaryFile()
-    self.tmpfile2 = tempfile.NamedTemporaryFile()
-    utils.WriteFile(self.tmpfile2.name, data="Hello World\n")
-    self.results = {
-      self.tmpfile.name: "da39a3ee5e6b4b0d3255bfef95601890afd80709",
-      self.tmpfile2.name: "648a6a6ffffdaa0badb23b8baf90b6168dd16b3a",
-      }
-
-  def testSingleFile(self):
-    self.assertEqual(utils._FingerprintFile(self.tmpfile.name),
-                     self.results[self.tmpfile.name])
-
-    self.assertEqual(utils._FingerprintFile("/no/such/file"), None)
-
-  def testBigFile(self):
-    self.tmpfile.write("A" * 8192)
-    self.tmpfile.flush()
-    self.assertEqual(utils._FingerprintFile(self.tmpfile.name),
-                     "35b6795ca20d6dc0aff8c7c110c96cd1070b8c38")
-
-  def testMultiple(self):
-    all_files = self.results.keys()
-    all_files.append("/no/such/file")
-    self.assertEqual(utils.FingerprintFiles(self.results.keys()), self.results)
-
-
 class TestGenerateSelfSignedX509Cert(unittest.TestCase):
   def setUp(self):
     self.tmpdir = tempfile.mkdtemp()
@@ -1829,55 +1801,6 @@ class TestVerifyCertificateInner(unittest.TestCase):
     self.assertEqual(errcode, utils.CERT_ERROR)
 
 
-class TestHmacFunctions(unittest.TestCase):
-  # Digests can be checked with "openssl sha1 -hmac $key"
-  def testSha1Hmac(self):
-    self.assertEqual(utils.Sha1Hmac("", ""),
-                     "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d")
-    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", "Hello World"),
-                     "ef4f3bda82212ecb2f7ce868888a19092481f1fd")
-    self.assertEqual(utils.Sha1Hmac("TguMTA2K", ""),
-                     "f904c2476527c6d3e6609ab683c66fa0652cb1dc")
-
-    longtext = 1500 * "The quick brown fox jumps over the lazy dog\n"
-    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", longtext),
-                     "35901b9a3001a7cdcf8e0e9d7c2e79df2223af54")
-
-  def testSha1HmacSalt(self):
-    self.assertEqual(utils.Sha1Hmac("TguMTA2K", "", salt="abc0"),
-                     "4999bf342470eadb11dfcd24ca5680cf9fd7cdce")
-    self.assertEqual(utils.Sha1Hmac("TguMTA2K", "", salt="abc9"),
-                     "17a4adc34d69c0d367d4ffbef96fd41d4df7a6e8")
-    self.assertEqual(utils.Sha1Hmac("3YzMxZWE", "Hello World", salt="xyz0"),
-                     "7f264f8114c9066afc9bb7636e1786d996d3cc0d")
-
-  def testVerifySha1Hmac(self):
-    self.assert_(utils.VerifySha1Hmac("", "", ("fbdb1d1b18aa6c08324b"
-                                               "7d64b71fb76370690e1d")))
-    self.assert_(utils.VerifySha1Hmac("TguMTA2K", "",
-                                      ("f904c2476527c6d3e660"
-                                       "9ab683c66fa0652cb1dc")))
-
-    digest = "ef4f3bda82212ecb2f7ce868888a19092481f1fd"
-    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World", digest))
-    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
-                                      digest.lower()))
-    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
-                                      digest.upper()))
-    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
-                                      digest.title()))
-
-  def testVerifySha1HmacSalt(self):
-    self.assert_(utils.VerifySha1Hmac("TguMTA2K", "",
-                                      ("17a4adc34d69c0d367d4"
-                                       "ffbef96fd41d4df7a6e8"),
-                                      salt="abc9"))
-    self.assert_(utils.VerifySha1Hmac("3YzMxZWE", "Hello World",
-                                      ("7f264f8114c9066afc9b"
-                                       "b7636e1786d996d3cc0d"),
-                                      salt="xyz0"))
-
-
 class TestIgnoreSignals(unittest.TestCase):
   """Test the IgnoreSignals decorator"""