 
 extension Crypto {
     
-    public static func md5(_ data: Data) -> Data {
+    public static func md5<S : ContiguousBytes>(_ data: S) -> Data {
         var digest = Data(count: Int(CC_MD5_DIGEST_LENGTH))
-        digest.withUnsafeMutableBytes { digest in data.withUnsafeBytes { _ = CC_MD5($0.baseAddress, CC_LONG(data.count), digest.baseAddress!.assumingMemoryBound(to: UInt8.self)) } }
-        return digest
-    }
-    
-    public static func sha1(_ data: Data) -> Data {
+        digest.withUnsafeMutableBufferPointer { digest in data.withUnsafeBytes { _ = CC_MD5($0.baseAddress, CC_LONG($0.count), digest.baseAddress) } }
+        return digest
+    }
+    
+    public static func sha1<S : ContiguousBytes>(_ data: S) -> Data {
         var digest = Data(count: Int(CC_SHA1_DIGEST_LENGTH))
-        digest.withUnsafeMutableBytes { digest in data.withUnsafeBytes { _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), digest.baseAddress!.assumingMemoryBound(to: UInt8.self)) } }
-        return digest
-    }
-    
-    public static func sha224(_ data: Data) -> Data {
+        digest.withUnsafeMutableBufferPointer { digest in data.withUnsafeBytes { _ = CC_SHA1($0.baseAddress, CC_LONG($0.count), digest.baseAddress) } }
+        return digest
+    }
+    
+    public static func sha224<S : ContiguousBytes>(_ data: S) -> Data {
         var digest = Data(count: Int(CC_SHA224_DIGEST_LENGTH))
-        digest.withUnsafeMutableBytes { digest in data.withUnsafeBytes { _ = CC_SHA224($0.baseAddress, CC_LONG(data.count), digest.baseAddress!.assumingMemoryBound(to: UInt8.self)) } }
-        return digest
-    }
-    
-    public static func sha256(_ data: Data) -> Data {
+        digest.withUnsafeMutableBufferPointer { digest in data.withUnsafeBytes { _ = CC_SHA224($0.baseAddress, CC_LONG($0.count), digest.baseAddress) } }
+        return digest
+    }
+    
+    public static func sha256<S : ContiguousBytes>(_ data: S) -> Data {
         var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
-        digest.withUnsafeMutableBytes { digest in data.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), digest.baseAddress!.assumingMemoryBound(to: UInt8.self)) } }
-        return digest
-    }
-    
-    public static func sha384(_ data: Data) -> Data {
+        digest.withUnsafeMutableBufferPointer { digest in data.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG($0.count), digest.baseAddress) } }
+        return digest
+    }
+    
+    public static func sha384<S : ContiguousBytes>(_ data: S) -> Data {
         var digest = Data(count: Int(CC_SHA384_DIGEST_LENGTH))
-        digest.withUnsafeMutableBytes { digest in data.withUnsafeBytes { _ = CC_SHA384($0.baseAddress, CC_LONG(data.count), digest.baseAddress!.assumingMemoryBound(to: UInt8.self)) } }
-        return digest
-    }
-    
-    public static func sha512(_ data: Data) -> Data {
+        digest.withUnsafeMutableBufferPointer { digest in data.withUnsafeBytes { _ = CC_SHA384($0.baseAddress, CC_LONG($0.count), digest.baseAddress) } }
+        return digest
+    }
+    
+    public static func sha512<S : ContiguousBytes>(_ data: S) -> Data {
         var digest = Data(count: Int(CC_SHA512_DIGEST_LENGTH))
-        digest.withUnsafeMutableBytes { digest in data.withUnsafeBytes { _ = CC_SHA512($0.baseAddress, CC_LONG(data.count), digest.baseAddress!.assumingMemoryBound(to: UInt8.self)) } }
+        digest.withUnsafeMutableBufferPointer { digest in data.withUnsafeBytes { _ = CC_SHA512($0.baseAddress, CC_LONG($0.count), digest.baseAddress) } }
         return digest
     }
 }

        return Int(CCCalibratePBKDF(algorithm.rawValue, keyLength, saltLength, pseudoRandomAlgorithm.rawValue, derivedKeyLength, UInt32(msec)))
    }
    
    public static func PBKDF(_ algorithm: PBKDFAlgorithm, _ pseudoRandomAlgorithm: PseudoRandomAlgorithm, _ key: Data, _ salt: Data, _ derivedKeyLength: Int, _ round: Int) throws -> Data {
        
        var result = Data(count: derivedKeyLength)
        
        let status = result.withUnsafeMutableBufferPointer { result in key.withUnsafeBufferPointer { key in key.withMemoryRebound(to: Int8.self) { key in salt.withUnsafeBufferPointer { salt in
            
            CCKeyDerivationPBKDF(algorithm.rawValue, key.baseAddress, key.count, salt.baseAddress, salt.count, pseudoRandomAlgorithm.rawValue, uint(round), result.baseAddress, result.count)
            
            } } } }
        
        guard status == kCCSuccess else  { throw Error(status: status) }
        

        return Data(referencing: immutableReference)
    }()
    
    public static func symmetricKeyWrap<K : ContiguousBytes, E : ContiguousBytes>(_ algorithm: WrappingAlgorithm, _ key: K, _ encryptionKey: E) throws -> Data {
        return try symmetricKeyWrap(algorithm, key, encryptionKey, rfc3394)
    }
    
    public static func symmetricKeyWrap<K : ContiguousBytes, E : ContiguousBytes, I : ContiguousBytes>(_ algorithm: WrappingAlgorithm, _ key: K, _ encryptionKey: E, _ iv: I) throws -> Data {
        
        return try key.withUnsafeBytes { key in try encryptionKey.withUnsafeBytes { encryptionKey in try iv.withUnsafeBytes { iv in
            
            let outputSize = CCSymmetricWrappedSize(algorithm.rawValue, key.count)
            var result = Data(count: outputSize)
            
            var length = outputSize
            
            let status = result.withUnsafeMutableBufferPointer { CCSymmetricKeyWrap(algorithm.rawValue, iv.bindMemory(to: UInt8.self).baseAddress, iv.count, encryptionKey.bindMemory(to: UInt8.self).baseAddress, encryptionKey.count, key.bindMemory(to: UInt8.self).baseAddress, key.count, $0.baseAddress, &length) }
            
            guard status == kCCSuccess else  { throw Error(status: status) }
            
            return result.prefix(upTo: length)
            
            } } }
    }
    
    public static func symmetricKeyUnwrap<K : ContiguousBytes, E : ContiguousBytes>(_ algorithm: WrappingAlgorithm, _ key: K, _ encryptionKey: E) throws -> Data {
        return try symmetricKeyUnwrap(algorithm, key, encryptionKey, rfc3394)
    }
    
    public static func symmetricKeyUnwrap<K : ContiguousBytes, E : ContiguousBytes, I : ContiguousBytes>(_ algorithm: WrappingAlgorithm, _ key: K, _ encryptionKey: E, _ iv: I) throws -> Data {
        
        return try key.withUnsafeBytes { key in try encryptionKey.withUnsafeBytes { encryptionKey in try iv.withUnsafeBytes { iv in
            
            let outputSize = CCSymmetricUnwrappedSize(algorithm.rawValue, key.count)
            var result = Data(count: outputSize)
            
            var length = outputSize
            
            let status = result.withUnsafeMutableBufferPointer { CCSymmetricKeyUnwrap(algorithm.rawValue, iv.bindMemory(to: UInt8.self).baseAddress, iv.count, encryptionKey.bindMemory(to: UInt8.self).baseAddress, encryptionKey.count, key.bindMemory(to: UInt8.self).baseAddress, key.count, $0.baseAddress, &length) }
            
            guard status == kCCSuccess else  { throw Error(status: status) }
            
            return result.prefix(upTo: length)
            
            } } }
    }
}

