Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
RockfordWei committed Oct 18, 2017
0 parents commit ef2f573
Show file tree
Hide file tree
Showing 16 changed files with 2,683 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.DS_Store
/.build*
/Packages
/*.xcodeproj
/*.resolved
/*.pins
/*.sh
/PADocker*
/*.orig

31 changes: 31 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "PerfectBCrypt",
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "PerfectBCrypt",
targets: ["PerfectBCrypt"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "bcrypt",
dependencies: []),
.target(
name: "PerfectBCrypt",
dependencies: ["bcrypt"]),
.testTarget(
name: "PerfectBCryptTests",
dependencies: ["PerfectBCrypt"]),
]
)
112 changes: 112 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Perfect-BCrypt

<p align="center">
<a href="http://perfect.org/get-involved.html" target="_blank">
<img src="http://perfect.org/assets/github/perfect_github_2_0_0.jpg" alt="Get Involed with Perfect!" width="854" />
</a>
</p>

<p align="center">
<a href="https://github.com/PerfectlySoft/Perfect" target="_blank">
<img src="http://www.perfect.org/github/Perfect_GH_button_1_Star.jpg" alt="Star Perfect On Github" />
</a>
<a href="http://stackoverflow.com/questions/tagged/perfect" target="_blank">
<img src="http://www.perfect.org/github/perfect_gh_button_2_SO.jpg" alt="Stack Overflow" />
</a>
<a href="https://twitter.com/perfectlysoft" target="_blank">
<img src="http://www.perfect.org/github/Perfect_GH_button_3_twit.jpg" alt="Follow Perfect on Twitter" />
</a>
<a href="http://perfect.ly" target="_blank">
<img src="http://www.perfect.org/github/Perfect_GH_button_4_slack.jpg" alt="Join the Perfect Slack" />
</a>
</p>

<p align="center">
<a href="https://developer.apple.com/swift/" target="_blank">
<img src="https://img.shields.io/badge/Swift-4.0-orange.svg?style=flat" alt="Swift 4.0">
</a>
<a href="https://developer.apple.com/swift/" target="_blank">
<img src="https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat" alt="Platforms OS X | Linux">
</a>
<a href="http://perfect.org/licensing.html" target="_blank">
<img src="https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat" alt="License Apache">
</a>
<a href="http://twitter.com/PerfectlySoft" target="_blank">
<img src="https://img.shields.io/badge/[email protected]?style=flat" alt="PerfectlySoft Twitter">
</a>
<a href="http://perfect.ly" target="_blank">
<img src="http://perfect.ly/badge.svg" alt="Slack Status">
</a>
</p>


##Acknowledgement

Perfect-BCrypt module is a Swift Wrapper of [bcrypt](https://github.com/pyca/bcrypt)

## Issues

We are transitioning to using JIRA for all bugs and support related issues, therefore the GitHub issues has been disabled.

If you find a mistake, bug, or any other helpful suggestion you'd like to make on the docs please head over to [http://jira.perfect.org:8080/servicedesk/customer/portal/1](http://jira.perfect.org:8080/servicedesk/customer/portal/1) and raise it.

A comprehensive list of open issues can be found at [http://jira.perfect.org:8080/projects/ISS/issues](http://jira.perfect.org:8080/projects/ISS/issues)

## Building

Add this project as a dependency in your Package.swift file.

```
.Package(url: "https://github.com/PerfectSideRepo/Perfect-BCrypt.git",
majorVersion: 3)
```

## Quick Start


### `BCrypt.Salt()`

Generate a "salt" string, for example, `let salt = BCrypt.Salt()` will return something like `$2b$12$yfG5ZTnTjg.HcgcI2o6Nhe`

### `BCrypt.Hash()`

Generate shadow by password and salt. For example:

``` swift
let password = "Kk4DQuMMfZL9o"
let salt = "$2b$04$cVWp4XaNU8a4v1uMRum2SO"
let hashed = try BCrypt.Hash(password, salt: salt)

// the hashed result will be:
// "$2b$04$cVWp4XaNU8a4v1uMRum2SO026BWLIoQMD/TXg5uZV.0P.uO8m3YEm"
```


### `BCrypt.Check()`

Verify a password if matches with the previously generated hash:

``` swift
guard BCrypt.Check(password, hashed: shadow)) else {
// Access Denied.
}
```

### `BCrypt.KDF()`

KDF is used in OpenSSH's newer encrypted private key format:

``` swift
let derived = try BCrypt.KDF("password", salt: "salt",
desiredKeyBytes: 32, rounds: 4, ignoreFewRounds: true)

// derived will be a 32 byte UInt8 array
// 0x5b, 0xbf, 0x0c, 0xc2, 0x93, 0x58, 0x7f, 0x1c,
// 0x36, 0x35, 0x55, 0x5c, 0x27, 0x79, 0x65, 0x98,
// 0xd4, 0x7e, 0x57, 0x90, 0x71, 0xbf, 0x42, 0x7e,
// 0x9d, 0x8f, 0xbe, 0x84, 0x2a, 0xba, 0x34, 0xd9
```

## Further Information

For more documentation, please visit [perfect.org](http://www.perfect.org/docs/crypto.html).
145 changes: 145 additions & 0 deletions Sources/PerfectBCrypt/PerfectBCrypt.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import bcrypt
import Foundation

open class BCrypt {

public enum SaltPrefixType:String {
case _2A = "2a"
case _2B = "2b"
}

public enum Exception: Error {
case InvalidRounds
case RandomAllocationFault
case UTF8Fault
case Unsupported
case InvalidSalt
case InvalidPassword
case KDFault
case RandomDeviceFault
}

public static func RandomArray<T>(count: Int) throws ->[T] {
guard let frandom = fopen("/dev/urandom", "rb") else {
throw Exception.RandomDeviceFault
}
let size = MemoryLayout<T>.size * count
var buf = UnsafeMutablePointer<T>.allocate(capacity: size)
defer { buf.deallocate(capacity: size) }
guard size == fread(buf, MemoryLayout<T>.size, count, frandom) else {
throw Exception.RandomAllocationFault
}
_ = fclose(frandom)
let array = UnsafeMutableBufferPointer<T>(start: buf, count: count)
return Array(array)
}
/// Generate salt based on the settings
/// - parameters:
/// - prefix: 2A or 2B
/// - rounds: a number in [4, 31]
/// - returns: a salted string.
/// - throws: Exception
public static func Salt(_ prefix: SaltPrefixType = ._2B,
rounds: Int = 12) throws -> String {
guard rounds > 3 && rounds < 32 else {
throw Exception.InvalidRounds
}
let salt:[UInt8] = try RandomArray(count:16)
let size = 30
let outputPointer = UnsafeMutablePointer<Int8>.allocate(capacity: size)
defer { outputPointer.deallocate(capacity: size) }
_ = encode_base64(outputPointer, salt, 16)
guard let output = String(validatingUTF8: outputPointer) else {
throw Exception.RandomAllocationFault
}
let rnd = String(format: "%2.2u", rounds)
return "$" + prefix.rawValue + "$" + rnd + "$" + output
}

/// Generate shadow by password and salt
/// - parameters:
/// - password: the password to hash
/// - salt: the salt to add with
/// - returns: a salted password
/// - throws: Exception
public static func Hash(_ password: String, salt: String) throws -> String {
let size = 128
let hashed = UnsafeMutablePointer<Int8>.allocate(capacity: size)
defer { hashed.deallocate(capacity: size) }
guard 0 == bcrypt_hashpass(password, salt, hashed, size) else {
throw Exception.InvalidSalt
}
guard let ret = (salt.withCString { pSalt -> String? in
memcpy(hashed, pSalt, 4)
return String(validatingUTF8: hashed)
}) else { throw Exception.UTF8Fault }
return ret
}

public static func KDF(_ password: String, salt: String, desiredKeyBytes: Int, rounds: UInt32, ignoreFewRounds: Bool = false) throws -> [UInt8] {
guard !password.isEmpty else {
throw Exception.InvalidPassword
}
guard !salt.isEmpty else {
throw Exception.InvalidSalt
}
guard desiredKeyBytes > 0 && desiredKeyBytes <= 513 else {
throw Exception.Unsupported
}
guard rounds > 0 else {
throw Exception.InvalidRounds
}
if !ignoreFewRounds {
guard rounds > 49 else {
throw Exception.InvalidRounds
}
}
let key = UnsafeMutablePointer<UInt8>.allocate(capacity: desiredKeyBytes)
defer { key.deallocate(capacity: desiredKeyBytes) }
guard 0 == bcrypt_pbkdf(password, password.count, salt, salt.count, key, desiredKeyBytes, rounds) else {
throw Exception.KDFault
}
let buf = UnsafeMutableBufferPointer<UInt8>(start: key, count: desiredKeyBytes)
return Array(buf)
}

#if os(Linux)
/// Verify if the password matches the hashed string
/// - parameters:
/// - password: a password to test with
/// - hashed: a hashed string to test
/// - returns:
/// True if matches.
public static func Check(_ password: String, hashed: String) -> Bool {
do {
let ret = try Hash(password, salt: hashed)
guard ret.count == hashed.count else {
return false
}
return 0 == timingsafe_bcmp(ret, hashed, ret.count)
}catch {
return false
}
}
#else
/// Verify if the password matches the hashed string
/// - parameters:
/// - password: a password to test with
/// - hashed: a hashed string to test
/// - returns:
/// True if matches.
@available(OSX 10.12.1, *)
public static func Check(_ password: String, hashed: String) -> Bool {
do {
let ret = try Hash(password, salt: hashed)
guard ret.count == hashed.count else {
return false
}
return 0 == timingsafe_bcmp(ret, hashed, ret.count)
}catch {
return false
}
}
#endif

}
Loading

0 comments on commit ef2f573

Please sign in to comment.