This repository has been archived by the owner on Oct 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from vapor/first
First
- Loading branch information
Showing
15 changed files
with
1,041 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.DS_Store | ||
/.build | ||
/Packages | ||
/*.xcodeproj | ||
Package.pins | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// swift-tools-version:3.1 | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "BCrypt", | ||
dependencies: [ | ||
// Module for generating random bytes and numbers. | ||
.Package(url: "https://github.com/vapor/random.git", majorVersion: 0) | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,40 @@ | ||
# bcrypt | ||
|
||
![Swift](http://img.shields.io/badge/swift-3.1-brightgreen.svg) | ||
[![CircleCI](https://circleci.com/gh/vapor/bcrypt.svg?style=shield)](https://circleci.com/gh/vapor/bcrypt) | ||
[![Slack Status](http://vapor.team/badge.svg)](http://vapor.team) | ||
|
||
Swift implementation of the BCrypt password hashing function used in [Vapor](https://github.com/vapor/vapor)'s packages. | ||
|
||
## Usage | ||
|
||
### Hash | ||
|
||
```swift | ||
import BCrypt | ||
|
||
let digest = try BCrypt.Hash.make(message: "foo") | ||
print(digest.string) | ||
``` | ||
|
||
### Verify | ||
|
||
```swift | ||
import BCrypt | ||
|
||
let digest = "$2a$04$TI13sbmh3IHnmRepeEFoJOkVZWsn5S1O8QOwm8ZU5gNIpJog9pXZm" | ||
let result = try BCrypt.Hash.verify(message: "vapor", matches: digest) | ||
print(result) | ||
``` | ||
|
||
## 📖 Documentation | ||
|
||
Visit the Vapor web framework's [documentation](http://docs.vapor.codes) for instructions on how to use this package. | ||
|
||
## 💧 Community | ||
|
||
Join the welcoming community of fellow Vapor developers in [slack](http://vapor.team). | ||
|
||
## 🔧 Compatibility | ||
|
||
This package has been tested on macOS and Ubuntu. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
public enum BCryptError: String, Error { | ||
case invalidHash | ||
case invalidSaltByteCount | ||
case invalidSaltVersion | ||
case invalidSaltCost | ||
case unsupportedSaltVersion | ||
} | ||
|
||
import Debugging | ||
|
||
extension BCryptError: Debuggable { | ||
public var reason: String { | ||
switch self { | ||
case .invalidHash: | ||
return "The hash being parsed does not match the recognized format" | ||
case .invalidSaltByteCount: | ||
return "BCrypt salt requires 16 bytes" | ||
case .invalidSaltVersion: | ||
return "Invalid salt version format" | ||
case .invalidSaltCost: | ||
return "Invalid salt cost format" | ||
case .unsupportedSaltVersion: | ||
return "Unsupported salt version" | ||
} | ||
} | ||
|
||
public var identifier: String { | ||
return rawValue | ||
} | ||
|
||
public var possibleCauses: [String] { | ||
return [ | ||
"BCrypt hash being parsed is not in the format `$2x$xx$ssssssssssssssssssssssddddddddddddddddddddddddddddddd`", | ||
"BCrypt hash is not base64 encoded properly" | ||
] | ||
} | ||
|
||
public var suggestedFixes: [String] { | ||
return [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import Core | ||
|
||
/// Base64 extension for BCrypt. This is a weird base64 since instead of using | ||
/// /+ for the last two characters, or the urlEncoded -_, it uses /. | ||
struct Base64 { | ||
static let encodingTable : [Byte] = [ | ||
.period, .forwardSlash, .A, .B, .C, .D, .E, .F, .G, .H, .I, .J, .K, | ||
.L, .M, .N, .O, .P, .Q, .R, .S, .T, .U, .V, .W, .X, | ||
.Y, .Z, .a, .b, .c, .d, .e, .f, .g, .h, .i, .j, .k, | ||
.l, .m, .n, .o, .p, .q, .r, .s, .t, .u, .v, .w, .x, | ||
.y, .z, .zero, .one, .two, .three, .four, .five, .six, .seven, .eight, .nine | ||
] | ||
|
||
static let decodingTable : [Byte] = [ | ||
.max, .max, .max, .max, .max, .max, .max, .max, .max, .max, | ||
.max, .max, .max, .max, .max, .max, .max, .max, .max, .max, | ||
.max, .max, .max, .max, .max, .max, .max, .max, .max, .max, | ||
.max, .max, .max, .max, .max, .max, .max, .max, .max, .max, | ||
.max, .max, .max, .max, .max, .max, 0, 1, 54, 55, | ||
56, 57, 58, 59, 60, 61, 62, 63, .max, .max, | ||
.max, .max, .max, .max, .max, 2, 3, 4, 5, 6, | ||
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, | ||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, | ||
27, .max, .max, .max, .max, .max, .max, 28, 29, 30, | ||
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | ||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, | ||
51, 52, 53, .max, .max, .max, .max, .max | ||
] | ||
|
||
static func encode(_ bytes: Bytes, count: UInt) -> Bytes { | ||
if bytes.count == 0 || count == 0 { | ||
return [] | ||
} | ||
|
||
var len: Int = Int(count) | ||
if len > bytes.count { | ||
len = bytes.count | ||
} | ||
|
||
var offset: Int = 0 | ||
var c1: UInt8 | ||
var c2: UInt8 | ||
var result: Bytes = [] | ||
|
||
while offset < len { | ||
c1 = bytes[offset] & 0xff | ||
offset += 1 | ||
result.append(encodingTable[Int((c1 >> 2) & 0x3f)]) | ||
c1 = (c1 & 0x03) << 4 | ||
if offset >= len { | ||
result.append(encodingTable[Int(c1 & 0x3f)]) | ||
break | ||
} | ||
|
||
c2 = bytes[offset] & 0xff | ||
offset += 1 | ||
c1 |= (c2 >> 4) & 0x0f | ||
result.append(encodingTable[Int(c1 & 0x3f)]) | ||
c1 = (c2 & 0x0f) << 2 | ||
if offset >= len { | ||
result.append(encodingTable[Int(c1 & 0x3f)]) | ||
break | ||
} | ||
|
||
c2 = bytes[offset] & 0xff | ||
offset += 1 | ||
c1 |= (c2 >> 6) & 0x03 | ||
result.append(encodingTable[Int(c1 & 0x3f)]) | ||
result.append(encodingTable[Int(c2 & 0x3f)]) | ||
} | ||
|
||
return result | ||
} | ||
|
||
private static func char64of(x: Byte) -> Byte { | ||
if x < 0 || x > 128 - 1 { | ||
// The character would go out of bounds of the pre-calculated array so return -1. | ||
return Byte.max | ||
} | ||
|
||
// Return the matching Base64 encoded character. | ||
return decodingTable[Int(x)] | ||
} | ||
|
||
static func decode(_ s: Bytes, count maxolen: UInt) -> Bytes { | ||
let maxolen = Int(maxolen) | ||
|
||
var off: Int = 0 | ||
var olen: Int = 0 | ||
var result = Bytes(repeating: 0, count: maxolen) | ||
|
||
var c1: Byte | ||
var c2: Byte | ||
var c3: Byte | ||
var c4: Byte | ||
var o: Byte | ||
|
||
while off < s.count - 1 && olen < maxolen { | ||
c1 = char64of(x: s[off]) | ||
off += 1 | ||
c2 = char64of(x: s[off]) | ||
off += 1 | ||
if c1 == Byte.max || c2 == Byte.max { | ||
break | ||
} | ||
|
||
o = c1 << 2 | ||
o |= (c2 & 0x30) >> 4 | ||
result[olen] = o | ||
olen += 1 | ||
if olen >= maxolen || off >= s.count { | ||
break | ||
} | ||
|
||
c3 = char64of(x: s[Int(off)]) | ||
off += 1 | ||
|
||
if c3 == Byte.max { | ||
break | ||
} | ||
|
||
o = (c2 & 0x0f) << 4 | ||
o |= (c3 & 0x3c) >> 2 | ||
result[olen] = o | ||
olen += 1 | ||
if olen >= maxolen || off >= s.count { | ||
break | ||
} | ||
|
||
c4 = char64of(x: s[off]) | ||
off += 1 | ||
o = (c3 & 0x03) << 6 | ||
o |= c4 | ||
result[olen] = o | ||
olen += 1 | ||
} | ||
|
||
return result[0..<olen].array | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import Core | ||
|
||
// MARK: Serializer | ||
|
||
extension Hash { | ||
public static func make(message: Bytes, with salt: Salt? = nil) throws -> Bytes { | ||
let hash = try Hash(salt) | ||
let digest = hash.digest(message: message) | ||
let serializer = Serializer(hash.salt, digest: digest) | ||
return serializer.serialize() | ||
} | ||
|
||
public static func make(message: BytesConvertible, with salt: Salt? = nil) throws -> Bytes { | ||
return try make( | ||
message: message.makeBytes(), | ||
with: salt | ||
) | ||
} | ||
} | ||
|
||
// MARK: Parser | ||
|
||
extension Hash { | ||
public static func verify(message: Bytes, matches input: Bytes) throws -> Bool { | ||
let parser = try Parser(input) | ||
let salt = try parser.parseSalt() | ||
let hasher = try Hash(salt) | ||
let testDigest = hasher.digest(message: message) | ||
return try testDigest == parser.parseDigest() ?? [] | ||
} | ||
|
||
public static func verify(message: BytesConvertible, matches digest: BytesConvertible) throws -> Bool { | ||
return try verify( | ||
message: message.makeBytes(), | ||
matches: digest.makeBytes() | ||
) | ||
} | ||
|
||
public static func verify(message: Bytes, matches digest: BytesConvertible) throws -> Bool { | ||
return try verify( | ||
message: message, | ||
matches: digest.makeBytes() | ||
) | ||
} | ||
|
||
public static func verify(message: BytesConvertible, matches digest: Bytes) throws -> Bool { | ||
return try verify( | ||
message: message.makeBytes(), | ||
matches: digest | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
@_exported import Core |
Oops, something went wrong.