 
 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 Data(referencing: immutableReference)
    }()
    
    public static func symmetricKeyWrap(_ algorithm: WrappingAlgorithm, _ key: Data, _ encryptionKey: Data, _ iv: Data = Crypto.rfc3394) throws -> Data {
        
        let outputSize = CCSymmetricWrappedSize(algorithm.rawValue, key.count)
        var result = Data(count: outputSize)
        
        var length = outputSize
        
        let status = result.withUnsafeMutableBufferPointer { result in key.withUnsafeBufferPointer { key in encryptionKey.withUnsafeBufferPointer { encryptionKey in iv.withUnsafeBufferPointer { iv in
            
            CCSymmetricKeyWrap(algorithm.rawValue, iv.baseAddress, iv.count, encryptionKey.baseAddress, encryptionKey.count, key.baseAddress, key.count, result.baseAddress, &length)
            
            } } } }
        
        guard status == kCCSuccess else  { throw Error(status: status) }
        
        return result.prefix(upTo: length)
        
    }
    
    public static func symmetricKeyUnwrap(_ algorithm: WrappingAlgorithm, _ key: Data, _ encryptionKey: Data, _ iv: Data = Crypto.rfc3394) throws -> Data {
        
        let outputSize = CCSymmetricUnwrappedSize(algorithm.rawValue, key.count)
        var result = Data(count: outputSize)
        
        var length = outputSize
        
        let status = result.withUnsafeMutableBufferPointer { result in key.withUnsafeBufferPointer { key in encryptionKey.withUnsafeBufferPointer { encryptionKey in iv.withUnsafeBufferPointer { iv in
            
            CCSymmetricKeyUnwrap(algorithm.rawValue, iv.baseAddress, iv.count, encryptionKey.baseAddress, encryptionKey.count, key.baseAddress, key.count, result.baseAddress, &length)
            
            } } } }
        
        guard status == kCCSuccess else  { throw Error(status: status) }
        
        return result.prefix(upTo: length)
        
    }
}


        public var status: CCCryptorStatus
    }
    
    public static func encrypt<K : ContiguousBytes, D : ContiguousBytes>(_ algorithm: CryptorAlgorithm, _ options: CryptorOptions, _ key: K, _ data: D) throws -> Data {
        return try encrypt(algorithm, options, key, data, EmptyCollection())
    }
    
    public static func encrypt<K : ContiguousBytes, D : ContiguousBytes, I : ContiguousBytes>(_ algorithm: CryptorAlgorithm, _ options: CryptorOptions, _ key: K, _ data: D, _ iv: I) throws -> Data {
        
        return try key.withUnsafeBytes { key in try data.withUnsafeBytes { data in try iv.withUnsafeBytes { iv in
            
            var result = Data(count: data.count + algorithm.blockSize)
            var length = 0
            
            let status = result.withUnsafeMutableBytes { CCCrypt(CCOperation(kCCEncrypt), algorithm.rawValue, CCOptions(options.rawValue), key.baseAddress, key.count, iv.baseAddress, data.baseAddress, data.count, $0.baseAddress, $0.count, &length) }
            
            guard status == kCCSuccess else  { throw Error(status: status) }
            
            return result.prefix(length)
            
            } } }
    }
    
    public static func decrypt<K : ContiguousBytes, D : ContiguousBytes>(_ algorithm: CryptorAlgorithm, _ options: CryptorOptions, _ key: K, _ data: D) throws -> Data {
        return try decrypt(algorithm, options, key, data, EmptyCollection())
    }
    
    public static func decrypt<K : ContiguousBytes, D : ContiguousBytes, I : ContiguousBytes>(_ algorithm: CryptorAlgorithm, _ options: CryptorOptions, _ key: K, _ data: D, _ iv: I) throws -> Data {
        
        return try key.withUnsafeBytes { key in try data.withUnsafeBytes { data in try iv.withUnsafeBytes { iv in
            
            var result = Data(count: data.count + algorithm.blockSize)
            var length = 0
            
            let status = result.withUnsafeMutableBytes { CCCrypt(CCOperation(kCCDecrypt), algorithm.rawValue, CCOptions(options.rawValue), key.baseAddress, key.count, iv.baseAddress, data.baseAddress, data.count, $0.baseAddress, $0.count, &length) }
            
            guard status == kCCSuccess else  { throw Error(status: status) }
            
            return result.prefix(length)
            
            } } }
    }
}

