diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index 8bc62dfd..862012ac 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -49,6 +49,7 @@ objc_library( "WiFiCommon/GNCNWFrameworkServerSocket.m", "WiFiCommon/GNCNWFrameworkServerSocket+Internal.h", "WiFiCommon/GNCNWFrameworkSocket.m", + "WiFiCommon/GNCNWParameters.m", ], hdrs = [ "BLEv2/GNCBLEError.h", @@ -75,6 +76,7 @@ objc_library( "WiFiCommon/GNCNWFrameworkError.h", "WiFiCommon/GNCNWFrameworkServerSocket.h", "WiFiCommon/GNCNWFrameworkSocket.h", + "WiFiCommon/GNCNWParameters.h", ], deps = [ "//internal/encoding:base85", @@ -88,6 +90,7 @@ objc_library( "//third_party/apple_frameworks:Network", "//third_party/apple_frameworks:NetworkExtension", "//third_party/apple_frameworks:ObjectiveC", + "//third_party/apple_frameworks:Security", "//third_party/apple_frameworks:SystemConfiguration", "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", ], diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h new file mode 100644 index 00000000..f2b8147f --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h @@ -0,0 +1,35 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/// Builds a non-TLS `nw_parameters_t` for the given parameters. +/// +/// @param includePeerToPeer Whether to include peer-to-peer (P2P) parameters. +FOUNDATION_EXPORT +nw_parameters_t _Nullable GNCBuildNonTLSParameters(BOOL includePeerToPeer) NS_RETURNS_RETAINED; + +/// Builds a TLS `nw_parameters_t` for the given parameters. +/// +/// @param PSK The PSK to use for the TLS parameters. +/// @param identity The identity to use for the TLS parameters. +/// @param includePeerToPeer Whether to include peer-to-peer (P2P) parameters. +FOUNDATION_EXPORT +nw_parameters_t _Nullable GNCBuildTLSParameters(NSData *PSK, NSData *identity, + BOOL includePeerToPeer) NS_RETURNS_RETAINED; + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.m b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.m new file mode 100644 index 00000000..b27fa8f5 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.m @@ -0,0 +1,92 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h" + +#import +#import +#import + +#import "GoogleToolboxForMac/GTMLogger.h" + +NS_ASSUME_NONNULL_BEGIN + +BOOL GNCConfigureTLSOptions(sec_protocol_options_t options, NSData *PSK, NSData *identity) { + if (!options) { + GTMLoggerError(@"[GNCNWParameters] Invalid parameter of options."); + return NO; + } + + if (PSK.length == 0 || identity.length == 0) { + GTMLoggerError(@"[GNCNWParameters] Invalid parameter of psk or identity."); + return NO; + } + + // Use tls_protocol_version_TLSv12 as TLS protocol version, which is the only version support PSK + // on iOS. + sec_protocol_options_set_min_tls_protocol_version(options, tls_protocol_version_TLSv12); + sec_protocol_options_set_max_tls_protocol_version(options, tls_protocol_version_TLSv12); + + sec_protocol_options_append_tls_ciphersuite(options, TLS_PSK_WITH_AES_128_GCM_SHA256); + + // Using DISPATCH_DATA_DESTRUCTOR_DEFAULT will cause dispatch_data_create() to explicitly make a + // copy of the data, and delete the copy when the dispatch_data_t is deallocated. + dispatch_data_t psk_secret_dispatch_data = + dispatch_data_create([PSK bytes], [PSK length], nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + dispatch_data_t psk_identity_dispatch_data = dispatch_data_create( + [identity bytes], [identity length], nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + if (!psk_secret_dispatch_data || !psk_identity_dispatch_data) { + GTMLoggerError(@"[GNCNWParameters] Failed to create dispatch_data_t for PSK."); + return NO; + } + + // Add the Pre-Shared Key and its identity to the TLS options + sec_protocol_options_add_pre_shared_key(options, psk_secret_dispatch_data, + psk_identity_dispatch_data); + + GTMLoggerInfo(@"[GNCNWParameters] Successfully configured TLS options."); + return YES; +} + +nw_parameters_t _Nullable GNCBuildNonTLSParameters(BOOL includePeerToPeer) { + nw_parameters_t parameters = + nw_parameters_create_secure_tcp(/*tls*/ NW_PARAMETERS_DISABLE_PROTOCOL, + /*tcp*/ NW_PARAMETERS_DEFAULT_CONFIGURATION); + nw_parameters_set_include_peer_to_peer(parameters, includePeerToPeer); + return parameters; +} + +nw_parameters_t _Nullable GNCBuildTLSParameters(NSData *PSK, NSData *identity, + BOOL includePeerToPeer) { + __block BOOL TLSWasConfigured = NO; + nw_parameters_t parameters = nw_parameters_create_secure_tcp( + ^(nw_protocol_options_t default_tls_options) { + // Configure the TLS protocol options. + sec_protocol_options_t sec_protocol_options = + nw_tls_copy_sec_protocol_options(default_tls_options); + TLSWasConfigured = GNCConfigureTLSOptions(sec_protocol_options, PSK, identity); + }, + /*tcp*/ NW_PARAMETERS_DEFAULT_CONFIGURATION); + if (!TLSWasConfigured) { + GTMLoggerError(@"[GNCNWParameters] Failed to configure TLS options."); + return nil; + } + + nw_parameters_set_include_peer_to_peer(parameters, includePeerToPeer); + return parameters; +} + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index efde10a3..d1d6cafa 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -47,6 +47,7 @@ objc_library( "GNCFakePeripheralManager.m", "GNCIPAddressTest.mm", "GNCMultiThreadExecutorTest.mm", + "GNCNWParametersTest.m", "GNCPlatformTest.mm", "GNCScheduledExecutorTest.mm", "GNCSingleThreadExecutorTest.mm", diff --git a/internal/platform/implementation/apple/Tests/GNCNWParametersTest.m b/internal/platform/implementation/apple/Tests/GNCNWParametersTest.m new file mode 100644 index 00000000..8ad55469 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCNWParametersTest.m @@ -0,0 +1,49 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h" + +#import +#import + +@interface GNCNWParametersTest : XCTestCase +@end + +@implementation GNCNWParametersTest + +- (void)testNonTLSParameters { + nw_parameters_t params = GNCBuildNonTLSParameters(YES); + XCTAssertTrue(nw_parameters_get_include_peer_to_peer(params)); +} + +- (void)testTLSParametersWithEmptyPSK { + nw_parameters_t params = + GNCBuildTLSParameters([@"" dataUsingEncoding:NSUTF8StringEncoding], + [@"nearby" dataUsingEncoding:NSUTF8StringEncoding], YES); + XCTAssertNil(params); +} + +- (void)testTLSParameters { + nw_parameters_t params = + GNCBuildTLSParameters([@"12345678" dataUsingEncoding:NSUTF8StringEncoding], + [@"nearby" dataUsingEncoding:NSUTF8StringEncoding], YES); + XCTAssertNotNil(params); + nw_protocol_stack_t protocol_stack = nw_parameters_copy_default_protocol_stack(params); + nw_protocol_options_t options = nw_protocol_stack_copy_transport_protocol(protocol_stack); + sec_protocol_options_t sec_protocol_options = nw_tls_copy_sec_protocol_options(options); + XCTAssertNotNil(sec_protocol_options); + XCTAssertTrue(nw_parameters_get_include_peer_to_peer(params)); +} + +@end