-
-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Don't use IP addresses for SNI (#119)
- Loading branch information
1 parent
d8230ea
commit 3af54d0
Showing
4 changed files
with
207 additions
and
2 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,19 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Vapor open source project | ||
// | ||
// Copyright (c) 2017-2022 Vapor project authors | ||
// Licensed under MIT | ||
// | ||
// See LICENSE for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
This product contains a derivation of `NIOSSLTestHelpers.swift` from SwiftNIO SSL. | ||
|
||
* LICENSE (Apache License 2.0): | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* HOMEPAGE: | ||
* https://github.com/apple/swift-nio-ssl |
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
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,142 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is based on NIOSSLTestHelpers.swift from | ||
// the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
@_implementationOnly import CNIOBoringSSL | ||
@testable import NIOSSL | ||
|
||
// This function generates a random number suitable for use in an X509 | ||
// serial field. This needs to be a positive number less than 2^159 | ||
// (such that it will fit into 20 ASN.1 bytes). | ||
// This also needs to be portable across operating systems, and the easiest | ||
// way to do that is to use either getentropy() or read from urandom. Sadly | ||
// we need to support old Linuxes which may not possess getentropy as a syscall | ||
// (and definitely don't support it in glibc), so we need to read from urandom. | ||
// In the future we should just use getentropy and be happy. | ||
func randomSerialNumber() -> ASN1_INTEGER { | ||
let bytesToRead = 20 | ||
let fd = open("/dev/urandom", O_RDONLY) | ||
precondition(fd != -1) | ||
defer { | ||
close(fd) | ||
} | ||
|
||
var readBytes = Array.init(repeating: UInt8(0), count: bytesToRead) | ||
let readCount = readBytes.withUnsafeMutableBytes { | ||
return read(fd, $0.baseAddress, bytesToRead) | ||
} | ||
precondition(readCount == bytesToRead) | ||
|
||
// Our 20-byte number needs to be converted into an integer. This is | ||
// too big for Swift's numbers, but BoringSSL can handle it fine. | ||
let bn = CNIOBoringSSL_BN_new() | ||
defer { | ||
CNIOBoringSSL_BN_free(bn) | ||
} | ||
|
||
_ = readBytes.withUnsafeBufferPointer { | ||
CNIOBoringSSL_BN_bin2bn($0.baseAddress, $0.count, bn) | ||
} | ||
|
||
// We want to bitshift this right by 1 bit to ensure it's smaller than | ||
// 2^159. | ||
CNIOBoringSSL_BN_rshift1(bn, bn) | ||
|
||
// Now we can turn this into our ASN1_INTEGER. | ||
var asn1int = ASN1_INTEGER() | ||
CNIOBoringSSL_BN_to_ASN1_INTEGER(bn, &asn1int) | ||
|
||
return asn1int | ||
} | ||
|
||
func generateRSAPrivateKey() -> UnsafeMutablePointer<EVP_PKEY> { | ||
let exponent = CNIOBoringSSL_BN_new() | ||
defer { | ||
CNIOBoringSSL_BN_free(exponent) | ||
} | ||
|
||
CNIOBoringSSL_BN_set_u64(exponent, 0x10001) | ||
|
||
let rsa = CNIOBoringSSL_RSA_new()! | ||
let generateRC = CNIOBoringSSL_RSA_generate_key_ex(rsa, CInt(2048), exponent, nil) | ||
precondition(generateRC == 1) | ||
|
||
let pkey = CNIOBoringSSL_EVP_PKEY_new()! | ||
let assignRC = CNIOBoringSSL_EVP_PKEY_assign(pkey, EVP_PKEY_RSA, rsa) | ||
|
||
precondition(assignRC == 1) | ||
return pkey | ||
} | ||
|
||
func addExtension(x509: OpaquePointer, nid: CInt, value: String) { | ||
var extensionContext = X509V3_CTX() | ||
|
||
CNIOBoringSSL_X509V3_set_ctx(&extensionContext, x509, x509, nil, nil, 0) | ||
let ext = value.withCString { (pointer) in | ||
return CNIOBoringSSL_X509V3_EXT_nconf_nid(nil, &extensionContext, nid, UnsafeMutablePointer(mutating: pointer)) | ||
}! | ||
CNIOBoringSSL_X509_add_ext(x509, ext, -1) | ||
CNIOBoringSSL_X509_EXTENSION_free(ext) | ||
} | ||
|
||
func generateSelfSignedCert(keygenFunction: () -> UnsafeMutablePointer<EVP_PKEY> = generateRSAPrivateKey) -> (NIOSSLCertificate, NIOSSLPrivateKey) { | ||
let pkey = keygenFunction() | ||
let x = CNIOBoringSSL_X509_new()! | ||
CNIOBoringSSL_X509_set_version(x, 2) | ||
|
||
// NB: X509_set_serialNumber uses an internal copy of the ASN1_INTEGER, so this is | ||
// safe, there will be no use-after-free. | ||
var serial = randomSerialNumber() | ||
CNIOBoringSSL_X509_set_serialNumber(x, &serial) | ||
|
||
let notBefore = CNIOBoringSSL_ASN1_TIME_new()! | ||
var now = time(nil) | ||
CNIOBoringSSL_ASN1_TIME_set(notBefore, now) | ||
CNIOBoringSSL_X509_set_notBefore(x, notBefore) | ||
CNIOBoringSSL_ASN1_TIME_free(notBefore) | ||
|
||
now += 60 * 60 // Give ourselves an hour | ||
let notAfter = CNIOBoringSSL_ASN1_TIME_new()! | ||
CNIOBoringSSL_ASN1_TIME_set(notAfter, now) | ||
CNIOBoringSSL_X509_set_notAfter(x, notAfter) | ||
CNIOBoringSSL_ASN1_TIME_free(notAfter) | ||
|
||
CNIOBoringSSL_X509_set_pubkey(x, pkey) | ||
|
||
let commonName = "localhost" | ||
let name = CNIOBoringSSL_X509_get_subject_name(x) | ||
commonName.withCString { (pointer: UnsafePointer<Int8>) -> Void in | ||
pointer.withMemoryRebound(to: UInt8.self, capacity: commonName.lengthOfBytes(using: .utf8)) { (pointer: UnsafePointer<UInt8>) -> Void in | ||
CNIOBoringSSL_X509_NAME_add_entry_by_NID(name, | ||
NID_commonName, | ||
MBSTRING_UTF8, | ||
UnsafeMutablePointer(mutating: pointer), | ||
CInt(commonName.lengthOfBytes(using: .utf8)), | ||
-1, | ||
0) | ||
} | ||
} | ||
CNIOBoringSSL_X509_set_issuer_name(x, name) | ||
|
||
addExtension(x509: x, nid: NID_basic_constraints, value: "critical,CA:FALSE") | ||
addExtension(x509: x, nid: NID_subject_key_identifier, value: "hash") | ||
addExtension(x509: x, nid: NID_subject_alt_name, value: "DNS:localhost") | ||
addExtension(x509: x, nid: NID_ext_key_usage, value: "critical,serverAuth,clientAuth") | ||
|
||
CNIOBoringSSL_X509_sign(x, pkey, CNIOBoringSSL_EVP_sha256()) | ||
|
||
return (NIOSSLCertificate.fromUnsafePointer(takingOwnership: x), NIOSSLPrivateKey.fromUnsafePointer(takingOwnership: pkey)) | ||
} | ||
|
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