diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2017-03-22  Vladimir Shabanov  <vshabanoff@gmail.com>
+
+	* HsOpenSSL.cabal (Version): Bump version to 0.11.4.3
+
+	* OpenSSL/EVP/Digest.hsc (hmacLBS): added HMAC on lazy bytestrings,
+	by SX91 (#18).
+
 2017-03-11  Vladimir Shabanov  <vshabanoff@gmail.com>
 
 	* HsOpenSSL.cabal (Version): Bump version to 0.11.4.2
diff --git a/HsOpenSSL.cabal b/HsOpenSSL.cabal
--- a/HsOpenSSL.cabal
+++ b/HsOpenSSL.cabal
@@ -12,7 +12,7 @@
     <http://hackage.haskell.org/package/tls>, which is a pure Haskell
     implementation of SSL.
     .
-Version:       0.11.4.2
+Version:       0.11.4.3
 License:       PublicDomain
 License-File:  COPYING
 Author:        Adam Langley, Mikhail Vorozhtsov, PHO, Taru Karttunen
diff --git a/OpenSSL/EVP/Digest.hsc b/OpenSSL/EVP/Digest.hsc
--- a/OpenSSL/EVP/Digest.hsc
+++ b/OpenSSL/EVP/Digest.hsc
@@ -11,6 +11,7 @@
     , digestLBS
 
     , hmacBS
+    , hmacLBS
     , pkcs5_pbkdf2_hmac_sha1
     )
     where
@@ -98,6 +99,10 @@
        bufPtr bufLenPtr
      bufLen <- fromIntegral <$> peek bufLenPtr
      B8.packCStringLen (bufPtr, bufLen)
+
+hmacLBS :: Digest -> B8.ByteString -> L8.ByteString -> B8.ByteString
+hmacLBS md key input
+    = unsafePerformIO $ hmacLazily md key input >>= hmacFinalBS
 
 -- | Calculate a PKCS5-PBKDF2 SHA1-HMAC suitable for password hashing.
 pkcs5_pbkdf2_hmac_sha1 :: B8.ByteString -- ^ password
diff --git a/OpenSSL/EVP/Internal.hsc b/OpenSSL/EVP/Internal.hsc
--- a/OpenSSL/EVP/Internal.hsc
+++ b/OpenSSL/EVP/Internal.hsc
@@ -35,6 +35,14 @@
     digestStrictly,
     digestLazily,
 
+    HmacCtx(..),
+    HMAC_CTX,
+    withHmacCtxPtr,
+
+    hmacUpdateBS,
+    hmacFinalBS,
+    hmacLazily,
+
     VaguePKey(..),
     EVP_PKEY,
     PKey(..),
@@ -77,6 +85,7 @@
 import System.IO.Unsafe (unsafeInterleaveIO)
 import OpenSSL.Utils
 
+
 {- EVP_CIPHER ---------------------------------------------------------------- -}
 
 -- |@Cipher@ is an opaque object that represents an algorithm of
@@ -312,6 +321,62 @@
 digestLazily md lbs = do
   ctx <- digestInit md
   mapM_ (digestUpdateBS ctx) $ L8.toChunks lbs
+  return ctx
+
+{- HMAC ---------------------------------------------------------------------- -}
+newtype HmacCtx = HmacCtx (ForeignPtr HMAC_CTX)
+data HMAC_CTX
+
+foreign import ccall unsafe "HsOpenSSL_HMAC_CTX_init"
+  _hmac_ctx_new :: IO (Ptr HMAC_CTX)
+
+foreign import ccall unsafe "HMAC_Init"
+  _hmac_init :: Ptr HMAC_CTX -> Ptr () -> CInt -> Ptr EVP_MD -> IO CInt
+
+foreign import ccall unsafe "HMAC_Update"
+  _hmac_update :: Ptr HMAC_CTX -> Ptr CChar -> CInt -> IO CInt
+
+foreign import ccall unsafe "HMAC_Final"
+  _hmac_final :: Ptr HMAC_CTX -> Ptr CChar -> Ptr CInt -> IO CUInt
+
+foreign import ccall unsafe "&HsOpenSSL_HMAC_CTX_free"
+  _hmac_ctx_free :: FunPtr (Ptr HMAC_CTX -> IO ())
+
+newHmacCtx :: IO HmacCtx
+newHmacCtx = do
+    ctxPtr <- _hmac_ctx_new
+    HmacCtx <$> newForeignPtr _hmac_ctx_free ctxPtr
+
+withHmacCtxPtr :: HmacCtx -> (Ptr HMAC_CTX -> IO a) -> IO a
+withHmacCtxPtr (HmacCtx ctx) = withForeignPtr ctx
+
+hmacInit :: Digest -> B8.ByteString -> IO HmacCtx
+hmacInit (Digest md) key = do
+  ctx <- newHmacCtx
+  withHmacCtxPtr ctx $ \ctxPtr ->
+    B8.unsafeUseAsCStringLen key $ \(keyPtr, keyLen) ->
+      _hmac_init ctxPtr (castPtr keyPtr) (fromIntegral keyLen) md
+        >>= failIf_ (/= 1)
+        >> return ctx
+
+hmacUpdateBS :: HmacCtx -> B8.ByteString -> IO ()
+hmacUpdateBS ctx bs = withHmacCtxPtr ctx $ \ctxPtr -> do
+  B8.unsafeUseAsCStringLen bs $ \(buf, len) ->
+    _hmac_update ctxPtr (castPtr buf) (fromIntegral len)
+      >>= failIf_ (/= 1)
+
+hmacFinalBS :: HmacCtx -> IO B8.ByteString
+hmacFinalBS ctx =
+  withHmacCtxPtr ctx $ \ctxPtr ->
+    B8.createAndTrim (#const EVP_MAX_MD_SIZE) $ \bufPtr ->
+      alloca $ \bufLenPtr -> do
+        _hmac_final ctxPtr (castPtr bufPtr) bufLenPtr >>= failIf_ (/= 1)
+        fromIntegral <$> peek bufLenPtr
+
+hmacLazily :: Digest -> B8.ByteString -> L8.ByteString -> IO HmacCtx
+hmacLazily md key lbs = do
+  ctx <- hmacInit md key
+  mapM_ (hmacUpdateBS ctx) $ L8.toChunks lbs
   return ctx
 
 {- EVP_PKEY ------------------------------------------------------------------ -}
diff --git a/cbits/HsOpenSSL.c b/cbits/HsOpenSSL.c
--- a/cbits/HsOpenSSL.c
+++ b/cbits/HsOpenSSL.c
@@ -70,6 +70,26 @@
     return EVP_CIPHER_iv_length(cipher);
 }
 
+/* EVP HMAC *******************************************************************/
+HMAC_CTX *HsOpenSSL_HMAC_CTX_new(void) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+    return HMAC_CTX_new();
+#else
+    HMAC_CTX *ctx = (HMAC_CTX *)malloc(sizeof(HMAC_CTX));
+    HMAC_CTX_init(ctx);
+    return ctx;
+#endif
+}
+
+void HsOpenSSL_HMAC_CTX_free(HMAC_CTX *ctx) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+    return HMAC_CTX_free();
+#else
+    HMAC_CTX_cleanup(ctx);
+    free(ctx);
+#endif
+}
+
 /* X509 ***********************************************************************/
 long HsOpenSSL_X509_get_version(X509* x509) {
     return X509_get_version(x509);
diff --git a/cbits/HsOpenSSL.h b/cbits/HsOpenSSL.h
--- a/cbits/HsOpenSSL.h
+++ b/cbits/HsOpenSSL.h
@@ -54,6 +54,10 @@
 int HsOpenSSL_EVP_CIPHER_CTX_block_size(EVP_CIPHER_CTX* ctx);
 int HsOpenSSL_EVP_CIPHER_iv_length(EVP_CIPHER* cipher);
 
+/* EVP HMAC *******************************************************************/
+HMAC_CTX *HsOpenSSL_HMAC_CTX_new(void);
+void HsOpenSSL_HMAC_CTX_free(HMAC_CTX *ctx);
+
 /* X509 ***********************************************************************/
 long HsOpenSSL_X509_get_version(X509* x509);
 ASN1_TIME* HsOpenSSL_X509_get_notBefore(X509* x509);
