From 5294cb63b9fa5c1355509df076ed19b3c3921379 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 22 Sep 2025 16:57:27 -0400 Subject: [PATCH 01/14] Start CryptoKit bring up of HKDF --- .../Interop.HKDF.cs | 134 ++++++++++++++++++ .../Interop.PAL_HashAlgorithm.cs | 11 ++ .../Interop.RSA.cs | 8 -- .../src/System.Security.Cryptography.csproj | 4 +- .../Security/Cryptography/HKDF.Apple.cs | 77 ++++++++++ .../entrypoints.c | 2 + .../pal_swiftbindings.h | 3 + .../pal_swiftbindings.swift | 127 ++++++++++++++++- 8 files changed, 355 insertions(+), 11 deletions(-) create mode 100644 src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs create mode 100644 src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs new file mode 100644 index 00000000000000..040f2c5ab6658c --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using System.Runtime.Versioning; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; +using Swift.Runtime; + +#pragma warning disable CS3016 // Arrays as attribute arguments are not CLS Compliant + +internal static partial class Interop +{ + internal static partial class AppleCrypto + { + internal static unsafe void HkdfExpand( + HashAlgorithmName hashAlgorithm, + ReadOnlySpan prk, + ReadOnlySpan info, + Span destination) + { + Debug.Assert(!destination.IsEmpty); + Debug.Assert(!prk.IsEmpty); + + PAL_HashAlgorithm algorithm = PalAlgorithmFromAlgorithmName(hashAlgorithm); + + fixed (byte* pPrk = prk) + fixed (byte* pInfo = &GetSwiftRef(info)) + fixed (byte* pDestination = destination) + { + AppleCryptoNative_HKDFExpand( + algorithm, + new UnsafeBufferPointer(pPrk, prk.Length), + new UnsafeBufferPointer(pInfo, info.Length), + new UnsafeMutableBufferPointer(pDestination, destination.Length), + out SwiftError error); + + if (error.Value != null) + { + throw new CryptographicException(); + } + } + } + + internal static unsafe void HKDFExtract( + HashAlgorithmName hashAlgorithm, + ReadOnlySpan ikm, + ReadOnlySpan salt, + Span destination) + { + Debug.Assert(!destination.IsEmpty); + + PAL_HashAlgorithm algorithm = PalAlgorithmFromAlgorithmName(hashAlgorithm); + + fixed (byte* pIkm = &GetSwiftRef(ikm)) + fixed (byte* pSalt = &GetSwiftRef(salt)) + fixed (byte* pDestination = destination) + { + AppleCryptoNative_HKDFExtract( + algorithm, + new UnsafeBufferPointer(pIkm, ikm.Length), + new UnsafeBufferPointer(pSalt, salt.Length), + new UnsafeMutableBufferPointer(pDestination, destination.Length), + out SwiftError error); + + if (error.Value != null) + { + throw new CryptographicException(); + } + } + } + + internal static unsafe void HKDFDeriveKey( + HashAlgorithmName hashAlgorithm, + ReadOnlySpan ikm, + ReadOnlySpan salt, + ReadOnlySpan info, + Span destination) + { + Debug.Assert(!destination.IsEmpty); + + PAL_HashAlgorithm algorithm = PalAlgorithmFromAlgorithmName(hashAlgorithm); + + fixed (byte* pIkm = &GetSwiftRef(ikm)) + fixed (byte* pSalt = &GetSwiftRef(salt)) + fixed (byte* pDestination = destination) + { + AppleCryptoNative_HKDFExtract( + algorithm, + new UnsafeBufferPointer(pIkm, ikm.Length), + new UnsafeBufferPointer(pSalt, salt.Length), + new UnsafeMutableBufferPointer(pDestination, destination.Length), + out SwiftError error); + + if (error.Value != null) + { + throw new CryptographicException(); + } + } + } + + [LibraryImport(Libraries.AppleCryptoNative)] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + private static unsafe partial void AppleCryptoNative_HKDFExpand( + PAL_HashAlgorithm hashAlgorithm, + UnsafeBufferPointer prk, + UnsafeBufferPointer info, + UnsafeMutableBufferPointer destination, + out SwiftError error); + + [LibraryImport(Libraries.AppleCryptoNative)] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + private static unsafe partial void AppleCryptoNative_HKDFExtract( + PAL_HashAlgorithm hashAlgorithm, + UnsafeBufferPointer ikm, + UnsafeBufferPointer salt, + UnsafeMutableBufferPointer destination, + out SwiftError error); + + [LibraryImport(Libraries.AppleCryptoNative)] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + private static unsafe partial void AppleCryptoNative_HKDFDeriveKey( + PAL_HashAlgorithm hashAlgorithm, + UnsafeBufferPointer ikm, + UnsafeBufferPointer salt, + UnsafeBufferPointer info, + UnsafeMutableBufferPointer destination, + out SwiftError error); + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.PAL_HashAlgorithm.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.PAL_HashAlgorithm.cs index 8a09c06c2e525c..e25ebf3789815f 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.PAL_HashAlgorithm.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.PAL_HashAlgorithm.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Security.Cryptography; + internal static partial class Interop { internal static partial class AppleCrypto @@ -14,5 +17,13 @@ internal enum PAL_HashAlgorithm Sha384, Sha512, } + + private static PAL_HashAlgorithm PalAlgorithmFromAlgorithmName(HashAlgorithmName hashAlgorithmName) => + hashAlgorithmName == HashAlgorithmName.MD5 ? PAL_HashAlgorithm.Md5 : + hashAlgorithmName == HashAlgorithmName.SHA1 ? PAL_HashAlgorithm.Sha1 : + hashAlgorithmName == HashAlgorithmName.SHA256 ? PAL_HashAlgorithm.Sha256 : + hashAlgorithmName == HashAlgorithmName.SHA384 ? PAL_HashAlgorithm.Sha384 : + hashAlgorithmName == HashAlgorithmName.SHA512 ? PAL_HashAlgorithm.Sha512 : + throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmName.Name); } } diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs index deab51eeb2bbda..95a861db6ebb98 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs @@ -348,13 +348,5 @@ ref MemoryMarshal.GetReference(source), return ProcessPrimitiveResponse(returnValue, cfData, cfError, destination, out bytesWritten); } - - private static PAL_HashAlgorithm PalAlgorithmFromAlgorithmName(HashAlgorithmName hashAlgorithmName) => - hashAlgorithmName == HashAlgorithmName.MD5 ? PAL_HashAlgorithm.Md5 : - hashAlgorithmName == HashAlgorithmName.SHA1 ? PAL_HashAlgorithm.Sha1 : - hashAlgorithmName == HashAlgorithmName.SHA256 ? PAL_HashAlgorithm.Sha256 : - hashAlgorithmName == HashAlgorithmName.SHA384 ? PAL_HashAlgorithm.Sha384 : - hashAlgorithmName == HashAlgorithmName.SHA512 ? PAL_HashAlgorithm.Sha512 : - throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmName.Name); } } diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 13ae5bdccba4f2..e83a0f09a8c95f 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -1334,6 +1334,8 @@ Link="Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ecc.cs" /> + - + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs new file mode 100644 index 00000000000000..f43b0d7e61ae7e --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Security.Cryptography +{ + public static partial class HKDF + { + private static readonly bool s_hasCryptoKitImplementation = + OperatingSystem.IsMacOS() || + OperatingSystem.IsMacCatalyst() || + OperatingSystem.IsIOSVersionAtLeast(14) || + OperatingSystem.IsTvOSVersionAtLeast(14); + + private static void Extract( + HashAlgorithmName hashAlgorithmName, + int hashLength, + ReadOnlySpan ikm, + ReadOnlySpan salt, + Span prk) + { + if (s_hasCryptoKitImplementation) + { + Interop.AppleCrypto.HKDFExtract(hashAlgorithmName, ikm, salt, prk); + } + else + { + HKDFManagedImplementation.Extract(hashAlgorithmName, hashLength, ikm, salt, prk); + } + } + + private static void Expand( + HashAlgorithmName hashAlgorithmName, + int hashLength, + ReadOnlySpan prk, + Span output, + ReadOnlySpan info) + { + if (s_hasCryptoKitImplementation) + { + Interop.AppleCrypto.HkdfExpand(hashAlgorithmName, prk, info, output); + } + else + { + HKDFManagedImplementation.Expand(hashAlgorithmName, hashLength, prk, output, info); + } + } + + private static void DeriveKeyCore( + HashAlgorithmName hashAlgorithmName, + int hashLength, + ReadOnlySpan ikm, + Span output, + ReadOnlySpan salt, + ReadOnlySpan info) + { + // if (s_hasCryptoKitImplementation) + // { + // Debug.Assert(Interop.Crypto.EvpKdfAlgs.Hkdf is not null); + // Debug.Assert(hashAlgorithmName.Name is not null); + + // Interop.Crypto.HkdfDeriveKey( + // Interop.Crypto.EvpKdfAlgs.Hkdf, + // ikm, + // hashAlgorithmName.Name, + // salt, + // info, + // output); + // } + // else + { + HKDFManagedImplementation.DeriveKey(hashAlgorithmName, hashLength, ikm, output, salt, info); + } + } + } +} diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c index d968ed2e127cf3..7502fc8bb2870e 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -43,6 +43,8 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_EccGetKeySizeInBits) DllImportEntry(AppleCryptoNative_EcdhKeyAgree) DllImportEntry(AppleCryptoNative_GetRandomBytes) + DllImportEntry(AppleCryptoNative_HKDFExpand) + DllImportEntry(AppleCryptoNative_HKDFExtract) DllImportEntry(AppleCryptoNative_HmacClone) DllImportEntry(AppleCryptoNative_HmacCreate) DllImportEntry(AppleCryptoNative_HmacCurrent) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h index 058c94869b57db..cf35380abb2627 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h @@ -11,3 +11,6 @@ EXTERN_C void* AppleCryptoNative_ChaCha20Poly1305Decrypt; EXTERN_C void* AppleCryptoNative_AesGcmEncrypt; EXTERN_C void* AppleCryptoNative_AesGcmDecrypt; EXTERN_C void* AppleCryptoNative_IsAuthenticationFailure; + +EXTERN_C void* AppleCryptoNative_HKDFExpand; +EXTERN_C void* AppleCryptoNative_HKDFExtract; diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 70a7176c7d7790..3110b948b825b5 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -118,7 +118,7 @@ public func AppleCryptoNative_ChaCha20Poly1305Encrypt( cipherText: cipherText, tag: tag, aad: aad) - } +} @_silgen_name("AppleCryptoNative_ChaCha20Poly1305Decrypt") @available(iOS 13, tvOS 13, *) @@ -158,7 +158,7 @@ public func AppleCryptoNative_AesGcmEncrypt( cipherText: cipherText, tag: tag, aad: aad) - } +} @_silgen_name("AppleCryptoNative_AesGcmDecrypt") @available(iOS 13, tvOS 13, *) @@ -193,3 +193,126 @@ public func AppleCryptoNative_IsAuthenticationFailure(error: Error) -> Bool { } return false } + +// Must remain in sync with PAL_HashAlgorithm from managed side. +enum PAL_HashAlgorithm: Int32 { + case unknown = 0 + case md5 = 1 + case sha1 = 2 + case sha256 = 3 + case sha384 = 4 + case sha512 = 5 +} + +enum HKDFError: Error { + case unknownHashAlgorithm +} + +@_silgen_name("AppleCryptoNative_HKDFExpand") +@available(iOS 14, tvOS 14, *) +public func AppleCryptoNative_HKDFExpand( + hashAlgorithm: Int32, + prk: UnsafeBufferPointer, + info: UnsafeBufferPointer, + destination: UnsafeMutableBufferPointer) throws { + + if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { + let key = try { + switch algorithm { + case .unknown: + throw HKDFError.unknownHashAlgorithm + case .md5: + HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + case .sha1: + HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + case .sha256: + HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + case .sha384: + HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + case .sha512: + HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + } + }() + + _ = key.withUnsafeBytes { keyBytes in + keyBytes.copyBytes(to: destination) + } + } + else { + throw HKDFError.unknownHashAlgorithm + } +} + +@_silgen_name("AppleCryptoNative_HKDFExtract") +@available(iOS 14, tvOS 14, *) +public func AppleCryptoNative_HKDFExtract( + hashAlgorithm: Int32, + ikm: UnsafeBufferPointer, + salt: UnsafeBufferPointer, + destination: UnsafeMutableBufferPointer) throws { + + let key = SymmetricKey(data: ikm) + + if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { + let prk : ContiguousBytes = try { + switch algorithm { + case .unknown: + throw HKDFError.unknownHashAlgorithm + case .md5: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha1: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha256: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha384: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha512: + HKDF.extract(inputKeyMaterial: key, salt: salt) + } + }() + + _ = prk.withUnsafeBytes { keyBytes in + keyBytes.copyBytes(to: destination) + } + } + else { + throw HKDFError.unknownHashAlgorithm + } +} + +@_silgen_name("AppleCryptoNative_HKDFDeriveKey") +@available(iOS 14, tvOS 14, *) +public func AppleCryptoNative_HKDFDeriveKey( + hashAlgorithm: Int32, + ikm: UnsafeBufferPointer, + salt: UnsafeBufferPointer, + destination: UnsafeMutableBufferPointer) throws { + + let key = SymmetricKey(data: ikm) + + if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { + let prk : ContiguousBytes = try { + switch algorithm { + case .unknown: + throw HKDFError.unknownHashAlgorithm + case .md5: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha1: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha256: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha384: + HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha512: + HKDF.extract(inputKeyMaterial: key, salt: salt) + } + }() + + _ = prk.withUnsafeBytes { keyBytes in + keyBytes.copyBytes(to: destination) + } + } + else { + throw HKDFError.unknownHashAlgorithm + } +} From e0033bf3075c6c726b90356935542ada67dcab61 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Mon, 22 Sep 2025 19:37:09 -0400 Subject: [PATCH 02/14] Finish derive key --- .../Interop.HKDF.cs | 4 ++- .../Security/Cryptography/HKDF.Apple.cs | 35 ++++++++++++------- .../entrypoints.c | 1 + .../pal_swiftbindings.h | 1 + .../pal_swiftbindings.swift | 15 ++++---- 5 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs index 040f2c5ab6658c..59128da2525d37 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs @@ -87,12 +87,14 @@ internal static unsafe void HKDFDeriveKey( fixed (byte* pIkm = &GetSwiftRef(ikm)) fixed (byte* pSalt = &GetSwiftRef(salt)) + fixed (byte* pInfo = &GetSwiftRef(info)) fixed (byte* pDestination = destination) { - AppleCryptoNative_HKDFExtract( + AppleCryptoNative_HKDFDeriveKey( algorithm, new UnsafeBufferPointer(pIkm, ikm.Length), new UnsafeBufferPointer(pSalt, salt.Length), + new UnsafeBufferPointer(pInfo, info.Length), new UnsafeMutableBufferPointer(pDestination, destination.Length), out SwiftError error); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs index f43b0d7e61ae7e..e4ee7fe593f55c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs @@ -20,6 +20,8 @@ private static void Extract( ReadOnlySpan salt, Span prk) { + ThrowForUnsupportedHashAlgorithm(hashAlgorithmName); + if (s_hasCryptoKitImplementation) { Interop.AppleCrypto.HKDFExtract(hashAlgorithmName, ikm, salt, prk); @@ -37,6 +39,8 @@ private static void Expand( Span output, ReadOnlySpan info) { + ThrowForUnsupportedHashAlgorithm(hashAlgorithmName); + if (s_hasCryptoKitImplementation) { Interop.AppleCrypto.HkdfExpand(hashAlgorithmName, prk, info, output); @@ -55,23 +59,28 @@ private static void DeriveKeyCore( ReadOnlySpan salt, ReadOnlySpan info) { - // if (s_hasCryptoKitImplementation) - // { - // Debug.Assert(Interop.Crypto.EvpKdfAlgs.Hkdf is not null); - // Debug.Assert(hashAlgorithmName.Name is not null); + ThrowForUnsupportedHashAlgorithm(hashAlgorithmName); - // Interop.Crypto.HkdfDeriveKey( - // Interop.Crypto.EvpKdfAlgs.Hkdf, - // ikm, - // hashAlgorithmName.Name, - // salt, - // info, - // output); - // } - // else + if (s_hasCryptoKitImplementation) + { + Interop.AppleCrypto.HKDFDeriveKey(hashAlgorithmName, ikm, salt, info, output); + } + else { HKDFManagedImplementation.DeriveKey(hashAlgorithmName, hashLength, ikm, output, salt, info); } } + + private static void ThrowForUnsupportedHashAlgorithm(HashAlgorithmName hashAlgorithmName) + { + if (hashAlgorithmName == HashAlgorithmName.SHA3_256 || hashAlgorithmName == HashAlgorithmName.SHA3_384 || + hashAlgorithmName == HashAlgorithmName.SHA3_512) + { + throw new PlatformNotSupportedException(); + } + + // Unknown alogorithms are handled outside of this as a CryptographicException. SHA-3 is known, its just + // not supported. + } } } diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c index 7502fc8bb2870e..1958ac1ca51f56 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -43,6 +43,7 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_EccGetKeySizeInBits) DllImportEntry(AppleCryptoNative_EcdhKeyAgree) DllImportEntry(AppleCryptoNative_GetRandomBytes) + DllImportEntry(AppleCryptoNative_HKDFDeriveKey) DllImportEntry(AppleCryptoNative_HKDFExpand) DllImportEntry(AppleCryptoNative_HKDFExtract) DllImportEntry(AppleCryptoNative_HmacClone) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h index cf35380abb2627..40f9ac92b8db78 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h @@ -12,5 +12,6 @@ EXTERN_C void* AppleCryptoNative_AesGcmEncrypt; EXTERN_C void* AppleCryptoNative_AesGcmDecrypt; EXTERN_C void* AppleCryptoNative_IsAuthenticationFailure; +EXTERN_C void* AppleCryptoNative_HKDFDeriveKey; EXTERN_C void* AppleCryptoNative_HKDFExpand; EXTERN_C void* AppleCryptoNative_HKDFExtract; diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 3110b948b825b5..770fadacb435f4 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -286,29 +286,30 @@ public func AppleCryptoNative_HKDFDeriveKey( hashAlgorithm: Int32, ikm: UnsafeBufferPointer, salt: UnsafeBufferPointer, + info: UnsafeBufferPointer, destination: UnsafeMutableBufferPointer) throws { let key = SymmetricKey(data: ikm) if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { - let prk : ContiguousBytes = try { + let derivedKey = try { switch algorithm { case .unknown: throw HKDFError.unknownHashAlgorithm case .md5: - HKDF.extract(inputKeyMaterial: key, salt: salt) + HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha1: - HKDF.extract(inputKeyMaterial: key, salt: salt) + HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha256: - HKDF.extract(inputKeyMaterial: key, salt: salt) + HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha384: - HKDF.extract(inputKeyMaterial: key, salt: salt) + HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha512: - HKDF.extract(inputKeyMaterial: key, salt: salt) + HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) } }() - _ = prk.withUnsafeBytes { keyBytes in + _ = derivedKey.withUnsafeBytes { keyBytes in keyBytes.copyBytes(to: destination) } } From 43f4b4c694ac1430e588223d3aea642520e9a02c Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 09:59:47 -0400 Subject: [PATCH 03/14] Add missing MD5 Expand test case --- .../tests/HKDFTests.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs index 460b81c9435727..7df76e903d42a4 100644 --- a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs @@ -95,6 +95,7 @@ public void ExtractEmptySalt() [Theory] [MemberData(nameof(GetHkdfTestCases))] + [MemberData(nameof(SupplementalTestCases))] public void ExpandTests(HkdfTestCase test) { byte[] okm = Expand(test.Hash, test.Prk, test.Okm.Length, test.Info); @@ -409,6 +410,36 @@ public static IEnumerable GetPrkTooShortTestCases() }, }; + public static IEnumerable SupplementalTestCases + { + get + { + if (MD5Supported) + { + yield return new object[] + { + // Generated + // openssl kdf -keylen 16 -kdfopt digest:MD5 -kdfopt hexkey:000102030405060708090A0B0C0D0E0F \ + // -kdfopt hexsalt:101112131415161718191A1B1C1D1E1F -kdfopt hexinfo:202122232425262728292A2B2C2D2E2F \ + // -binary HKDF | xxd -p + new HkdfTestCase() + { + Name = "Test with MD5, salt, and info", + Hash = HashAlgorithmName.MD5, + Ikm = "000102030405060708090A0B0C0D0E0F".HexToByteArray(), + Salt = "101112131415161718191A1B1C1D1E1F".HexToByteArray(), + Info = "202122232425262728292A2B2C2D2E2F".HexToByteArray(), + Okm = "5a25e9d9d27578f28a79a680fd9ce780".HexToByteArray(), + + // Add -kdfopt mode:EXTRACT_ONLY to derive the PRK. + Prk = "2d2d573fd48c9ad0be5e8214af0d7d64".HexToByteArray(), + + } + }; + } + } + } + public static IEnumerable Sha3TestCases { // These cases were generated from the openssl kdf command. From 7c67a3555ca542b07a5fc46b7dd0b7d5416a2379 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 10:02:14 -0400 Subject: [PATCH 04/14] Add tests for MD5 extract --- src/libraries/System.Security.Cryptography/tests/HKDFTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs index 7df76e903d42a4..9d32038fc9ff4a 100644 --- a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs @@ -19,6 +19,7 @@ public abstract class HKDFTests [Theory] [MemberData(nameof(GetHkdfTestCases))] + [MemberData(nameof(SupplementalTestCases))] public void ExtractTests(HkdfTestCase test) { byte[] prk = Extract(test.Hash, test.Prk.Length, test.Ikm, test.Salt); From 3e1fbe012b07b7ae2bcf75d404b81a80c1f55e4e Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 10:08:51 -0400 Subject: [PATCH 05/14] Add missing tests for DeriveKey MD5 --- src/libraries/System.Security.Cryptography/tests/HKDFTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs index 9d32038fc9ff4a..351808a668e91f 100644 --- a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs @@ -153,6 +153,7 @@ public void ExpandOkmMaxSize() [Theory] [MemberData(nameof(GetHkdfTestCases))] + [MemberData(nameof(SupplementalTestCases))] public void DeriveKeyTests(HkdfTestCase test) { byte[] okm = DeriveKey(test.Hash, test.Ikm, test.Okm.Length, test.Salt, test.Info); From 81888c626c5c1286cc1783631cd17c055b4bc5c7 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 10:47:21 -0400 Subject: [PATCH 06/14] Add missing test cases for SHA-384 and SHA-512 --- .../tests/HKDFTests.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs index 351808a668e91f..a656ec50c052de 100644 --- a/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/HKDFTests.cs @@ -439,6 +439,50 @@ public static IEnumerable SupplementalTestCases } }; } + + yield return new object[] + { + // Generated + // openssl kdf -keylen 16 -kdfopt digest:SHA384 -kdfopt hexkey:000102030405060708090A0B0C0D0E0F \ + // -kdfopt hexsalt:101112131415161718191A1B1C1D1E1F -kdfopt hexinfo:202122232425262728292A2B2C2D2E2F \ + // -binary HKDF | xxd -p + new HkdfTestCase() + { + Name = "Test with SHA-2-384, salt, and info", + Hash = HashAlgorithmName.SHA384, + Ikm = "000102030405060708090A0B0C0D0E0F".HexToByteArray(), + Salt = "101112131415161718191A1B1C1D1E1F".HexToByteArray(), + Info = "202122232425262728292A2B2C2D2E2F".HexToByteArray(), + Okm = "5b3a502e2ce5d366479afa17a4fe4aaa".HexToByteArray(), + + // Add -kdfopt mode:EXTRACT_ONLY to derive the PRK. + Prk = ("31ca88a527220f8271d78df4ce6c4d973f135ad37973b966" + + "44b4d52d499d0a2b03d53c875b1176b089e1e6161ab6d92b").HexToByteArray(), + + } + }; + + yield return new object[] + { + // Generated + // openssl kdf -keylen 16 -kdfopt digest:SHA512 -kdfopt hexkey:000102030405060708090A0B0C0D0E0F \ + // -kdfopt hexsalt:101112131415161718191A1B1C1D1E1F -kdfopt hexinfo:202122232425262728292A2B2C2D2E2F \ + // -binary HKDF | xxd -p + new HkdfTestCase() + { + Name = "Test with SHA-2-512, salt, and info", + Hash = HashAlgorithmName.SHA512, + Ikm = "000102030405060708090A0B0C0D0E0F".HexToByteArray(), + Salt = "101112131415161718191A1B1C1D1E1F".HexToByteArray(), + Info = "202122232425262728292A2B2C2D2E2F".HexToByteArray(), + Okm = "270b6ba8a5989e4c26c8d116930c14ab".HexToByteArray(), + + // Add -kdfopt mode:EXTRACT_ONLY to derive the PRK. + Prk = ("f6e6b1ddb24ea0f0ede0f533d1f350c86bf78966b0e5fd2af34dd00dae3901d6" + + "279fe8111d6572e3cd05f2f0eeabb9144dc0da9437cdf37b0c6d7f3b1064ab2b").HexToByteArray(), + + } + }; } } From d6dcded7e21641f0fc3f82b005c4a112009ddedc Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 10:49:30 -0400 Subject: [PATCH 07/14] Copy over docs fix --- .../src/System/Security/Cryptography/HKDF.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.cs index f167f18afc2466..454e0a77303477 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.cs @@ -171,7 +171,7 @@ public static byte[] DeriveKey(HashAlgorithmName hashAlgorithmName, byte[] ikm, /// The output buffer representing output keying material. /// The salt value (a non-secret random value). /// The context and application specific information (can be an empty span). - /// is empty, or is larger than the maximum allowed length. + /// is empty, or is larger than the maximum allowed length. public static void DeriveKey(HashAlgorithmName hashAlgorithmName, ReadOnlySpan ikm, Span output, ReadOnlySpan salt, ReadOnlySpan info) { int hashLength = Helpers.HashLength(hashAlgorithmName); From ed730360a7cc532948d8fcf907a4cbf38e2c7358 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 11:05:24 -0400 Subject: [PATCH 08/14] Add explicit return since Copilot wants it --- .../pal_swiftbindings.swift | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 770fadacb435f4..e59ae34ae443d3 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -222,15 +222,15 @@ public func AppleCryptoNative_HKDFExpand( case .unknown: throw HKDFError.unknownHashAlgorithm case .md5: - HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) case .sha1: - HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) case .sha256: - HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) case .sha384: - HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) case .sha512: - HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) } }() @@ -259,15 +259,15 @@ public func AppleCryptoNative_HKDFExtract( case .unknown: throw HKDFError.unknownHashAlgorithm case .md5: - HKDF.extract(inputKeyMaterial: key, salt: salt) + return HKDF.extract(inputKeyMaterial: key, salt: salt) case .sha1: - HKDF.extract(inputKeyMaterial: key, salt: salt) + return HKDF.extract(inputKeyMaterial: key, salt: salt) case .sha256: - HKDF.extract(inputKeyMaterial: key, salt: salt) + return HKDF.extract(inputKeyMaterial: key, salt: salt) case .sha384: - HKDF.extract(inputKeyMaterial: key, salt: salt) + return HKDF.extract(inputKeyMaterial: key, salt: salt) case .sha512: - HKDF.extract(inputKeyMaterial: key, salt: salt) + return HKDF.extract(inputKeyMaterial: key, salt: salt) } }() @@ -297,15 +297,15 @@ public func AppleCryptoNative_HKDFDeriveKey( case .unknown: throw HKDFError.unknownHashAlgorithm case .md5: - HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha1: - HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha256: - HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha384: - HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) case .sha512: - HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) } }() From 30d5f6ce68e38174b38ac4dedfa690342d8df432 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Tue, 23 Sep 2025 11:07:28 -0400 Subject: [PATCH 09/14] Fix typos. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/System/Security/Cryptography/HKDF.Apple.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs index e4ee7fe593f55c..00f6d36469124e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/HKDF.Apple.cs @@ -79,7 +79,7 @@ private static void ThrowForUnsupportedHashAlgorithm(HashAlgorithmName hashAlgor throw new PlatformNotSupportedException(); } - // Unknown alogorithms are handled outside of this as a CryptographicException. SHA-3 is known, its just + // Unknown algorithms are handled outside of this as a CryptographicException. SHA-3 is known, it's just // not supported. } } From f95a29b40086a79eea23fd51e9443125b74b169e Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 24 Sep 2025 17:36:19 -0400 Subject: [PATCH 10/14] Add debugging logs --- .../pal_swiftbindings.swift | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index e59ae34ae443d3..bceabb534f8ae1 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -208,6 +208,34 @@ enum HKDFError: Error { case unknownHashAlgorithm } +struct Logger { + private static let stdoutLock = NSLock() + + static func log(_ parts: Any..., separator: String = " ", terminator: String = "\n") { + let message = parts.map { String(describing: $0) }.joined(separator: separator) + terminator + + stdoutLock.lock() + defer { stdoutLock.unlock() } + + if let data = message.data(using: .utf8) { + FileHandle.standardOutput.write(data) + } + } + + static func hexEncode(_ bytes: C) -> String where C.Element == UInt8 { + var s = "" + s.reserveCapacity(64) + s.append("<\(bytes.count)> ") + for b in bytes { + s.append(String(UnicodeScalar(_hex[Int(b >> 4)]))) + s.append(String(UnicodeScalar(_hex[Int(b & 0x0F)]))) + } + return s + } + + private static let _hex: [UInt8] = Array("0123456789abcdef".utf8) +} + @_silgen_name("AppleCryptoNative_HKDFExpand") @available(iOS 14, tvOS 14, *) public func AppleCryptoNative_HKDFExpand( @@ -216,6 +244,8 @@ public func AppleCryptoNative_HKDFExpand( info: UnsafeBufferPointer, destination: UnsafeMutableBufferPointer) throws { + Logger.log("alg:", hashAlgorithm, "prk:", Logger.hexEncode(prk), "info:", Logger.hexEncode(info)) + if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { let key = try { switch algorithm { @@ -250,7 +280,7 @@ public func AppleCryptoNative_HKDFExtract( ikm: UnsafeBufferPointer, salt: UnsafeBufferPointer, destination: UnsafeMutableBufferPointer) throws { - + Logger.log("alg:", hashAlgorithm, "ikm:", Logger.hexEncode(ikm), "salt:", Logger.hexEncode(salt)) let key = SymmetricKey(data: ikm) if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { @@ -288,7 +318,7 @@ public func AppleCryptoNative_HKDFDeriveKey( salt: UnsafeBufferPointer, info: UnsafeBufferPointer, destination: UnsafeMutableBufferPointer) throws { - + Logger.log("alg:", hashAlgorithm, "ikm:", Logger.hexEncode(ikm), "salt:", Logger.hexEncode(info), "salt:", Logger.hexEncode(info)) let key = SymmetricKey(data: ikm) if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { From 54a9a456baf25650d597ea3786d4f80ca3c1ddce Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 24 Sep 2025 21:31:18 -0400 Subject: [PATCH 11/14] See if this is the Swift copyBytes bug --- .../pal_swiftbindings.swift | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index bceabb534f8ae1..017e7b7a11497a 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -264,8 +264,12 @@ public func AppleCryptoNative_HKDFExpand( } }() - _ = key.withUnsafeBytes { keyBytes in - keyBytes.copyBytes(to: destination) + // Copy the key in to a Data. + // This works around https://github.com/apple/swift-foundation/issues/638 which is fixed + // but not for iOS and macOS versions with older Swift runtimes. + key.withUnsafeBytes { keyBytes in + let keyBytesData = Data(keyBytes) + _ = keyBytesData.copyBytes(to: destination) } } else { @@ -301,8 +305,12 @@ public func AppleCryptoNative_HKDFExtract( } }() - _ = prk.withUnsafeBytes { keyBytes in - keyBytes.copyBytes(to: destination) + // Copy the prk in to a Data. + // This works around https://github.com/apple/swift-foundation/issues/638 which is fixed + // but not for iOS and macOS versions with older Swift runtimes. + prk.withUnsafeBytes { prkBytes in + let prkBytesData = Data(prkBytes) + _ = prkBytesData.copyBytes(to: destination) } } else { @@ -339,8 +347,9 @@ public func AppleCryptoNative_HKDFDeriveKey( } }() - _ = derivedKey.withUnsafeBytes { keyBytes in - keyBytes.copyBytes(to: destination) + derivedKey.withUnsafeBytes { keyBytes in + let keyBytesData = Data(keyBytes) + _ = keyBytesData.copyBytes(to: destination) } } else { From bcbb434f440b4138b31ef1bb3ac079fe70c8477e Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 25 Sep 2025 09:45:28 -0400 Subject: [PATCH 12/14] Revert data copying. --- .../pal_swiftbindings.swift | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 017e7b7a11497a..bceabb534f8ae1 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -264,12 +264,8 @@ public func AppleCryptoNative_HKDFExpand( } }() - // Copy the key in to a Data. - // This works around https://github.com/apple/swift-foundation/issues/638 which is fixed - // but not for iOS and macOS versions with older Swift runtimes. - key.withUnsafeBytes { keyBytes in - let keyBytesData = Data(keyBytes) - _ = keyBytesData.copyBytes(to: destination) + _ = key.withUnsafeBytes { keyBytes in + keyBytes.copyBytes(to: destination) } } else { @@ -305,12 +301,8 @@ public func AppleCryptoNative_HKDFExtract( } }() - // Copy the prk in to a Data. - // This works around https://github.com/apple/swift-foundation/issues/638 which is fixed - // but not for iOS and macOS versions with older Swift runtimes. - prk.withUnsafeBytes { prkBytes in - let prkBytesData = Data(prkBytes) - _ = prkBytesData.copyBytes(to: destination) + _ = prk.withUnsafeBytes { keyBytes in + keyBytes.copyBytes(to: destination) } } else { @@ -347,9 +339,8 @@ public func AppleCryptoNative_HKDFDeriveKey( } }() - derivedKey.withUnsafeBytes { keyBytes in - let keyBytesData = Data(keyBytes) - _ = keyBytesData.copyBytes(to: destination) + _ = derivedKey.withUnsafeBytes { keyBytes in + keyBytes.copyBytes(to: destination) } } else { From 09c8cefe6f03f434c9a725c28e0e65a563a4baec Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 25 Sep 2025 10:48:54 -0400 Subject: [PATCH 13/14] Try not using swift structs at p/invoke boundary --- .../Interop.HKDF.cs | 124 ++++++------ .../pal_swiftbindings.swift | 189 +++++++++++------- 2 files changed, 177 insertions(+), 136 deletions(-) diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs index 59128da2525d37..cc826b5a4f6235 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.HKDF.cs @@ -28,22 +28,21 @@ internal static unsafe void HkdfExpand( PAL_HashAlgorithm algorithm = PalAlgorithmFromAlgorithmName(hashAlgorithm); - fixed (byte* pPrk = prk) - fixed (byte* pInfo = &GetSwiftRef(info)) - fixed (byte* pDestination = destination) + int ret = AppleCryptoNative_HKDFExpand( + algorithm, + prk, + prk.Length, + info, + info.Length, + destination, + destination.Length); + + if (ret < 0) { - AppleCryptoNative_HKDFExpand( - algorithm, - new UnsafeBufferPointer(pPrk, prk.Length), - new UnsafeBufferPointer(pInfo, info.Length), - new UnsafeMutableBufferPointer(pDestination, destination.Length), - out SwiftError error); - - if (error.Value != null) - { - throw new CryptographicException(); - } + throw new CryptographicException(); } + + Debug.Assert(ret == destination.Length); } internal static unsafe void HKDFExtract( @@ -56,22 +55,21 @@ internal static unsafe void HKDFExtract( PAL_HashAlgorithm algorithm = PalAlgorithmFromAlgorithmName(hashAlgorithm); - fixed (byte* pIkm = &GetSwiftRef(ikm)) - fixed (byte* pSalt = &GetSwiftRef(salt)) - fixed (byte* pDestination = destination) + int ret = AppleCryptoNative_HKDFExtract( + algorithm, + ikm, + ikm.Length, + salt, + salt.Length, + destination, + destination.Length); + + if (ret < 0) { - AppleCryptoNative_HKDFExtract( - algorithm, - new UnsafeBufferPointer(pIkm, ikm.Length), - new UnsafeBufferPointer(pSalt, salt.Length), - new UnsafeMutableBufferPointer(pDestination, destination.Length), - out SwiftError error); - - if (error.Value != null) - { - throw new CryptographicException(); - } + throw new CryptographicException(); } + + Debug.Assert(ret == destination.Length); } internal static unsafe void HKDFDeriveKey( @@ -85,52 +83,58 @@ internal static unsafe void HKDFDeriveKey( PAL_HashAlgorithm algorithm = PalAlgorithmFromAlgorithmName(hashAlgorithm); - fixed (byte* pIkm = &GetSwiftRef(ikm)) - fixed (byte* pSalt = &GetSwiftRef(salt)) - fixed (byte* pInfo = &GetSwiftRef(info)) - fixed (byte* pDestination = destination) + int ret = AppleCryptoNative_HKDFDeriveKey( + algorithm, + ikm, + ikm.Length, + salt, + salt.Length, + info, + info.Length, + destination, + destination.Length); + + if (ret < 0) { - AppleCryptoNative_HKDFDeriveKey( - algorithm, - new UnsafeBufferPointer(pIkm, ikm.Length), - new UnsafeBufferPointer(pSalt, salt.Length), - new UnsafeBufferPointer(pInfo, info.Length), - new UnsafeMutableBufferPointer(pDestination, destination.Length), - out SwiftError error); - - if (error.Value != null) - { - throw new CryptographicException(); - } + throw new CryptographicException(); } + + Debug.Assert(ret == destination.Length); } [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial void AppleCryptoNative_HKDFExpand( + private static partial int AppleCryptoNative_HKDFExpand( PAL_HashAlgorithm hashAlgorithm, - UnsafeBufferPointer prk, - UnsafeBufferPointer info, - UnsafeMutableBufferPointer destination, - out SwiftError error); + ReadOnlySpan prkPtr, + int prkLength, + ReadOnlySpan infoPtr, + int infoLength, + Span destinationPtr, + int destinationLength); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial void AppleCryptoNative_HKDFExtract( + private static partial int AppleCryptoNative_HKDFExtract( PAL_HashAlgorithm hashAlgorithm, - UnsafeBufferPointer ikm, - UnsafeBufferPointer salt, - UnsafeMutableBufferPointer destination, - out SwiftError error); + ReadOnlySpan ikmPtr, + int ikmLength, + ReadOnlySpan saltPtr, + int saltLength, + Span destinationPtr, + int destinationLength); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial void AppleCryptoNative_HKDFDeriveKey( + private static partial int AppleCryptoNative_HKDFDeriveKey( PAL_HashAlgorithm hashAlgorithm, - UnsafeBufferPointer ikm, - UnsafeBufferPointer salt, - UnsafeBufferPointer info, - UnsafeMutableBufferPointer destination, - out SwiftError error); + ReadOnlySpan ikmPtr, + int ikmLength, + ReadOnlySpan saltPtr, + int saltLength, + ReadOnlySpan infoPtr, + int infoLength, + Span destinationPtr, + int destinationLength); } } diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index bceabb534f8ae1..5094859039430e 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -240,36 +240,47 @@ struct Logger { @available(iOS 14, tvOS 14, *) public func AppleCryptoNative_HKDFExpand( hashAlgorithm: Int32, - prk: UnsafeBufferPointer, - info: UnsafeBufferPointer, - destination: UnsafeMutableBufferPointer) throws { + prkPtr: UnsafeMutableRawPointer, + prkLength: Int32, + infoPtr: UnsafeMutableRawPointer, + infoLength: Int32, + destinationPtr: UnsafeMutablePointer, + destinationLength: Int32) -> Int32 { + + let prk = Data(bytesNoCopy: prkPtr, count: Int(prkLength), deallocator: Data.Deallocator.none) + let info = Data(bytesNoCopy: infoPtr, count: Int(infoLength), deallocator: Data.Deallocator.none) + let destinationLengthInt = Int(destinationLength) Logger.log("alg:", hashAlgorithm, "prk:", Logger.hexEncode(prk), "info:", Logger.hexEncode(info)) - if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { - let key = try { - switch algorithm { - case .unknown: - throw HKDFError.unknownHashAlgorithm - case .md5: - return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) - case .sha1: - return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) - case .sha256: - return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) - case .sha384: - return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) - case .sha512: - return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destination.count) - } - }() - - _ = key.withUnsafeBytes { keyBytes in - keyBytes.copyBytes(to: destination) + guard let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) else { + return -2 + } + + let keyFactory : () throws -> ContiguousBytes = { + switch algorithm { + case .unknown: + throw HKDFError.unknownHashAlgorithm + case .md5: + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destinationLengthInt) + case .sha1: + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destinationLengthInt) + case .sha256: + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destinationLengthInt) + case .sha384: + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destinationLengthInt) + case .sha512: + return HKDF.expand(pseudoRandomKey: prk, info: info, outputByteCount: destinationLengthInt) } } - else { - throw HKDFError.unknownHashAlgorithm + + guard let key = try? keyFactory() else { + return -1 + } + + return key.withUnsafeBytes { keyBytes in + let destination = UnsafeMutableRawBufferPointer(start: destinationPtr, count: destinationLengthInt) + return Int32(keyBytes.copyBytes(to: destination)) } } @@ -277,36 +288,48 @@ public func AppleCryptoNative_HKDFExpand( @available(iOS 14, tvOS 14, *) public func AppleCryptoNative_HKDFExtract( hashAlgorithm: Int32, - ikm: UnsafeBufferPointer, - salt: UnsafeBufferPointer, - destination: UnsafeMutableBufferPointer) throws { + ikmPtr: UnsafeMutableRawPointer, + ikmLength: Int32, + saltPtr: UnsafeMutableRawPointer, + saltLength: Int32, + destinationPtr: UnsafeMutablePointer, + destinationLength: Int32) -> Int32 { + + let ikm = Data(bytesNoCopy: ikmPtr, count: Int(ikmLength), deallocator: Data.Deallocator.none) + let salt = Data(bytesNoCopy: saltPtr, count: Int(saltLength), deallocator: Data.Deallocator.none) + let destinationLengthInt = Int(destinationLength) + Logger.log("alg:", hashAlgorithm, "ikm:", Logger.hexEncode(ikm), "salt:", Logger.hexEncode(salt)) let key = SymmetricKey(data: ikm) - if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { - let prk : ContiguousBytes = try { - switch algorithm { - case .unknown: - throw HKDFError.unknownHashAlgorithm - case .md5: - return HKDF.extract(inputKeyMaterial: key, salt: salt) - case .sha1: - return HKDF.extract(inputKeyMaterial: key, salt: salt) - case .sha256: - return HKDF.extract(inputKeyMaterial: key, salt: salt) - case .sha384: - return HKDF.extract(inputKeyMaterial: key, salt: salt) - case .sha512: - return HKDF.extract(inputKeyMaterial: key, salt: salt) - } - }() - - _ = prk.withUnsafeBytes { keyBytes in - keyBytes.copyBytes(to: destination) + guard let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) else { + return -2 + } + + let prkFactory : () throws -> ContiguousBytes = { + switch algorithm { + case .unknown: + throw HKDFError.unknownHashAlgorithm + case .md5: + return HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha1: + return HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha256: + return HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha384: + return HKDF.extract(inputKeyMaterial: key, salt: salt) + case .sha512: + return HKDF.extract(inputKeyMaterial: key, salt: salt) } } - else { - throw HKDFError.unknownHashAlgorithm + + guard let prk = try? prkFactory() else { + return -1 + } + + return prk.withUnsafeBytes { prkBytes in + let destination = UnsafeMutableRawBufferPointer(start: destinationPtr, count: destinationLengthInt) + return Int32(prkBytes.copyBytes(to: destination)) } } @@ -314,36 +337,50 @@ public func AppleCryptoNative_HKDFExtract( @available(iOS 14, tvOS 14, *) public func AppleCryptoNative_HKDFDeriveKey( hashAlgorithm: Int32, - ikm: UnsafeBufferPointer, - salt: UnsafeBufferPointer, - info: UnsafeBufferPointer, - destination: UnsafeMutableBufferPointer) throws { + ikmPtr: UnsafeMutableRawPointer, + ikmLength: Int32, + saltPtr: UnsafeMutableRawPointer, + saltLength: Int32, + infoPtr: UnsafeMutableRawPointer, + infoLength: Int32, + destinationPtr: UnsafeMutablePointer, + destinationLength: Int32) -> Int32 { + + let ikm = Data(bytesNoCopy: ikmPtr, count: Int(ikmLength), deallocator: Data.Deallocator.none) + let salt = Data(bytesNoCopy: saltPtr, count: Int(saltLength), deallocator: Data.Deallocator.none) + let info = Data(bytesNoCopy: infoPtr, count: Int(infoLength), deallocator: Data.Deallocator.none) + let destinationLengthInt = Int(destinationLength) + Logger.log("alg:", hashAlgorithm, "ikm:", Logger.hexEncode(ikm), "salt:", Logger.hexEncode(info), "salt:", Logger.hexEncode(info)) let key = SymmetricKey(data: ikm) - if let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) { - let derivedKey = try { - switch algorithm { - case .unknown: - throw HKDFError.unknownHashAlgorithm - case .md5: - return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) - case .sha1: - return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) - case .sha256: - return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) - case .sha384: - return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) - case .sha512: - return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destination.count) - } - }() - - _ = derivedKey.withUnsafeBytes { keyBytes in - keyBytes.copyBytes(to: destination) + guard let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) else { + return -2 + } + + let derivedKeyFactory : () throws -> ContiguousBytes = { + switch algorithm { + case .unknown: + throw HKDFError.unknownHashAlgorithm + case .md5: + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destinationLengthInt) + case .sha1: + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destinationLengthInt) + case .sha256: + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destinationLengthInt) + case .sha384: + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destinationLengthInt) + case .sha512: + return HKDF.deriveKey(inputKeyMaterial: key, salt: salt, info: info, outputByteCount: destinationLengthInt) } } - else { - throw HKDFError.unknownHashAlgorithm + + guard let derivedKey = try? derivedKeyFactory() else { + return -1 + } + + return derivedKey.withUnsafeBytes { keyBytes in + let destination = UnsafeMutableRawBufferPointer(start: destinationPtr, count: destinationLengthInt) + return Int32(keyBytes.copyBytes(to: destination)) } } From 06ed5d3140a67dc8931be16f9d6a96bd83c15a2d Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Thu, 25 Sep 2025 14:21:50 -0400 Subject: [PATCH 14/14] Remove logging / diagnostics --- .../pal_swiftbindings.swift | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 5094859039430e..0915b647c33f71 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -208,34 +208,6 @@ enum HKDFError: Error { case unknownHashAlgorithm } -struct Logger { - private static let stdoutLock = NSLock() - - static func log(_ parts: Any..., separator: String = " ", terminator: String = "\n") { - let message = parts.map { String(describing: $0) }.joined(separator: separator) + terminator - - stdoutLock.lock() - defer { stdoutLock.unlock() } - - if let data = message.data(using: .utf8) { - FileHandle.standardOutput.write(data) - } - } - - static func hexEncode(_ bytes: C) -> String where C.Element == UInt8 { - var s = "" - s.reserveCapacity(64) - s.append("<\(bytes.count)> ") - for b in bytes { - s.append(String(UnicodeScalar(_hex[Int(b >> 4)]))) - s.append(String(UnicodeScalar(_hex[Int(b & 0x0F)]))) - } - return s - } - - private static let _hex: [UInt8] = Array("0123456789abcdef".utf8) -} - @_silgen_name("AppleCryptoNative_HKDFExpand") @available(iOS 14, tvOS 14, *) public func AppleCryptoNative_HKDFExpand( @@ -251,8 +223,6 @@ public func AppleCryptoNative_HKDFExpand( let info = Data(bytesNoCopy: infoPtr, count: Int(infoLength), deallocator: Data.Deallocator.none) let destinationLengthInt = Int(destinationLength) - Logger.log("alg:", hashAlgorithm, "prk:", Logger.hexEncode(prk), "info:", Logger.hexEncode(info)) - guard let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) else { return -2 } @@ -298,8 +268,6 @@ public func AppleCryptoNative_HKDFExtract( let ikm = Data(bytesNoCopy: ikmPtr, count: Int(ikmLength), deallocator: Data.Deallocator.none) let salt = Data(bytesNoCopy: saltPtr, count: Int(saltLength), deallocator: Data.Deallocator.none) let destinationLengthInt = Int(destinationLength) - - Logger.log("alg:", hashAlgorithm, "ikm:", Logger.hexEncode(ikm), "salt:", Logger.hexEncode(salt)) let key = SymmetricKey(data: ikm) guard let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) else { @@ -350,8 +318,6 @@ public func AppleCryptoNative_HKDFDeriveKey( let salt = Data(bytesNoCopy: saltPtr, count: Int(saltLength), deallocator: Data.Deallocator.none) let info = Data(bytesNoCopy: infoPtr, count: Int(infoLength), deallocator: Data.Deallocator.none) let destinationLengthInt = Int(destinationLength) - - Logger.log("alg:", hashAlgorithm, "ikm:", Logger.hexEncode(ikm), "salt:", Logger.hexEncode(info), "salt:", Logger.hexEncode(info)) let key = SymmetricKey(data: ikm) guard let algorithm = PAL_HashAlgorithm(rawValue: hashAlgorithm) else {