vapor-docs/3.0/docs/crypto/hash.md

113 lines
3.5 KiB
Markdown

# Hash
Hashes are a one-directional encryption that is commonly used for validating files or one-way securing data such as passwords.
### Available hashes
Crypto currently supports a few hashes.
- MD5
- SHA1
- SHA2 (all variants)
MD5 and SHA1 are generally used for file validation or legacy (weak) passwords. They're performant and lightweight.
## Using the hashes
Every Hash type has a set of helpers that you can use.
## Hashing blobs of data
Every `Hash` has a static method called `hash` that can be used for hashing the entire contents of `Foundation.Data`, `ByteBuffer` or `String`.
The result is `Data` containing the resulting hash. The hash's length is according to spec and defined in the static variable `digestSize`.
```swift
// MD5 with `Data`
let fileData = Data()
let fileMD5 = MD5.hash(fileData)
// SHA1 with `ByteBuffer`
let fileBuffer: ByteBuffer = ...
let fileSHA1 = SHA1.hash(fileBuffer)
// SHA2 variants with String
let staticUnsafeToken: String = "rsadd14ndmasidfm12i4j"
let tokenHashSHA224 = SHA224.hash(staticUnsafeToken)
let tokenHashSHA256 = SHA256.hash(staticUnsafeToken)
let tokenHashSHA384 = SHA384.hash(staticUnsafeToken)
let tokenHashSHA512 = SHA512.hash(staticUnsafeToken)
```
## Incremental hashes (manual)
To incrementally process hashes you can create an instance of the Hash. This will set up a context.
All hash context initializers are empty:
```swift
// Create an MD5 context
let md5Context = MD5()
```
To process a single chunk of data, you can call the `update` function on a context using any `Sequence` of `UInt8`. That means `Array`, `Data` and `ByteBuffer` work alongside any other sequence of bytes.
```swift
md5Context.update(data)
```
The data data need not be a specific length. Any length works.
When you need the result, you can call `md5Context.finalize()`. This will finish calculating the hash by appending the standard `1` bit, padding and message bitlength.
You can optionally provide a last set of data to `finalize()`.
After calling `finalize()`, do not update the hash if you want correct results.
### Fetching the results
The context can then be accessed to extract the resulting Hash.
```swift
let hash: Data = md5Context.hash
```
## Streaming hashes (Async)
Sometimes you need to hash the contents of a Stream, for example, when processing a file transfer. In this case you can use `StreamHasher`.
First, create a new generic `StreamHasher<Hash>` where `Hash` is the hash you want to use. In this case, SHA512.
```swift
let streamHasher = StreamHasher<SHA512>()
```
This stream works like any `inputStream` by consuming the incoming data and passing the buffers to the hash context.
For example, draining a TCP socket.
```swift
let socket: TCP.Socket = ...
socket.drain(into: streamHasher)
```
This will incrementally update the hash using the provided TCP socket's data.
When the hash has been completely accumulated, you can `complete` the hash.
```swift
let hash: Data = streamHasher.complete()
```
This will reset the hash's context to the default configuration, ready to start over.
### Using StreamHasher as a transparent component
StreamHasher is only an `InputStream`, meaning it doesn't output anything. In some scenarios, like uploading a file, you want both the unaltered file data in addition to the hash. One solution is to split the stream. But that adds unnecessary complexity.
`PassthroughByteStreamHasher` is a `ByteStreamHasher` that's also an `OutputStream`. The output it emits is identical to the `PassthroughByteStreamHasher`'s input.
`PassthroughByteStreamHasher` functions exactly like `ByteStreamHasher`