Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e9de693
Add an external mu variant of the ML-DSA API (65 and 87 variants)
fpseverino Jun 4, 2025
e09d149
Revert "Add an external mu variant of the ML-DSA API (65 and 87 varia…
fpseverino Jun 16, 2025
e25c941
Add an external mu variant of the ML-DSA API (65 and 87 variants)
fpseverino Jun 4, 2025
e016958
Revert "Add an external mu variant of the ML-DSA API (65 and 87 varia…
fpseverino Jun 16, 2025
2688efb
Merge branch 'mldsa-external-mu' of https://github.com/fpseverino/swi…
fpseverino Jun 16, 2025
74c97de
Add external mu variant of ML-DSA to BoringSSL implementation
fpseverino Jun 16, 2025
a6213d7
Add external mu API to wrapper with `package` level
fpseverino Jun 16, 2025
bc935ac
Expose external mu API in CryptoExtras
fpseverino Jun 16, 2025
f8ed3ee
Add tests for external mu variant
fpseverino Jun 16, 2025
64aa6ca
Small formatting fixes
fpseverino Jun 16, 2025
fdb09e2
Use computed variable to get BoringSSL implementation
fpseverino Jun 16, 2025
f8bb7ef
Add `@testable` back in tests
fpseverino Jun 16, 2025
8f06719
Make the linter happy
fpseverino Jun 16, 2025
67074c0
Update CMakeLists and use FoundationEssentials
fpseverino Jun 17, 2025
2c12b1b
Merge branch 'wwdc-25' into mldsa-external-mu
fpseverino Jun 18, 2025
794dfa6
Update DocC
fpseverino Jun 19, 2025
8e57f3e
Merge branch 'wwdc-25' into mldsa-external-mu
fpseverino Jun 20, 2025
28350a9
Merge branch 'wwdc-25' into mldsa-external-mu
fpseverino Jun 23, 2025
63d96f1
Remove some warnings
fpseverino Jun 24, 2025
0434231
Format and remove warnings from MLKEM.swift
fpseverino Jun 24, 2025
66b1202
Remove `signature:` parameter label from `isValidSignature`
fpseverino Aug 6, 2025
478a430
Merge branch 'wwdc-25' into mldsa-external-mu
fpseverino Aug 18, 2025
8ba4cb8
Merge branch 'wwdc-25' into mldsa-external-mu
fpseverino Sep 9, 2025
5247e53
Merge branch 'wwdc-25' into mldsa-external-mu
fpseverino Sep 10, 2025
d9638b0
Fix conflicts
fpseverino Sep 10, 2025
4a6a254
Remove unreleated stuff
fpseverino Sep 10, 2025
f9fa563
Fix conflicts
fpseverino Sep 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ let package = Package(
.product(name: "SwiftASN1", package: "swift-asn1"),
],
exclude: privacyManifestExclude + [
"CMakeLists.txt"
"CMakeLists.txt",
"MLDSA/MLDSA+externalMu.swift.gyb",
],
resources: privacyManifestResource,
swiftSettings: swiftSettings
Expand Down
14 changes: 14 additions & 0 deletions Sources/Crypto/Docs.docc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,25 @@ Swift Crypto is built on top of [BoringSSL](https://boringssl.googlesource.com/b
- ``P256``
- ``SharedSecret``
- ``HPKE``
- ``MLDSA65``
- ``MLDSA87``

### Key derivation functions

- ``HKDF``

### Key encapsulation mechanisms (KEM)

- ``KEM``
- ``MLKEM768``
- ``MLKEM1024``
- ``XWingMLKEM768X25519``

### KEM keys

- ``KEMPrivateKey``
- ``KEMPublicKey``

### Errors

- ``CryptoKitError``
Expand Down
208 changes: 201 additions & 7 deletions Sources/Crypto/Signatures/BoringSSL/MLDSA_boring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ import Foundation
// any edits of this file WILL be overwritten and thus discarded
// see section `gyb` in `README` for details.

@_implementationOnly import CCryptoBoringSSL
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *)
extension MLDSA65 {
/// A ML-DSA-65 private key.
Expand Down Expand Up @@ -84,6 +77,17 @@ extension MLDSA65 {
try self.backing.signature(for: data, context: context)
}

/// Generate a signature for the prehashed message representative (a.k.a. "external mu").
///
/// > Note: The message representative should be obtained via calls to ``MLDSA65/PublicKey/prehash(for:context:)``.
///
/// - Parameter mu: The prehashed message representative (a.k.a. "external mu").
///
/// - Returns: The signature of the prehashed message representative.
func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data {
try self.backing.signature(forPrehashedMessageRepresentative: mu)
}

/// The size of the private key in bytes.
static let byteCount = Backing.byteCount

Expand Down Expand Up @@ -184,6 +188,38 @@ extension MLDSA65 {
return signature
}

/// Generate a signature for the prehashed message representative (a.k.a. "external mu").
///
/// > Note: The message representative should be obtained via calls to ``MLDSA65/PublicKey/prehash(for:context:)``.
///
/// - Parameter mu: The prehashed message representative (a.k.a. "external mu").
///
/// - Returns: The signature of the prehashed message representative.
func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data {
guard mu.count == MLDSA.muByteCount else {
throw CryptoKitError.incorrectParameterSize
}

var signature = Data(repeating: 0, count: MLDSA65.signatureByteCount)

let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in
let muBytes: ContiguousBytes = mu.regions.count == 1 ? mu.regions.first! : Array(mu)
return muBytes.withUnsafeBytes { muPtr in
CCryptoBoringSSL_MLDSA65_sign_message_representative(
signaturePtr.baseAddress,
&self.key,
muPtr.baseAddress
)
}
}

guard rc == 1 else {
throw CryptoKitError.internalBoringSSLError()
}

return signature
}

/// The size of the private key in bytes.
static let byteCount = Int(MLDSA65_PRIVATE_KEY_BYTES)
}
Expand Down Expand Up @@ -242,6 +278,27 @@ extension MLDSA65 {
self.backing.isValidSignature(signature, for: data, context: context)
}

/// Generate a prehashed message representative (a.k.a. "external mu") for the given message.
///
/// - Parameter data: The message to prehash.
///
/// - Returns: The prehashed message representative (a.k.a. "external mu").
func prehash<D: DataProtocol>(for data: D) throws -> Data {
let context: Data? = nil
return try self.backing.prehash(for: data, context: context)
}

/// Generate a prehashed message representative (a.k.a. "external mu") for the given message.
///
/// - Parameters:
/// - data: The message to prehash.
/// - context: The context of the message.
///
/// - Returns: The prehashed message representative (a.k.a. "external mu").
func prehash<D: DataProtocol, C: DataProtocol>(for data: D, context: C) throws -> Data {
try self.backing.prehash(for: data, context: context)
}

/// The size of the public key in bytes.
static let byteCount = Backing.byteCount

Expand Down Expand Up @@ -323,6 +380,41 @@ extension MLDSA65 {
}
}

/// Generate a prehashed message representative (a.k.a. "external mu") for the given message.
///
/// - Parameters:
/// - data: The message to prehash.
/// - context: The context of the message.
///
/// - Returns: The prehashed message representative (a.k.a. "external mu").
func prehash<D: DataProtocol, C: DataProtocol>(for data: D, context: C?) throws -> Data {
var mu = Data(repeating: 0, count: MLDSA.muByteCount)

let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
let rc: CInt = mu.withUnsafeMutableBytes { muPtr in
dataBytes.withUnsafeBytes { dataPtr in
context.withUnsafeBytes { contextPtr in
var prehash = MLDSA65_prehash()
let rc = CCryptoBoringSSL_MLDSA65_prehash_init(
&prehash,
&key,
contextPtr.baseAddress,
contextPtr.count
)
CCryptoBoringSSL_MLDSA65_prehash_update(&prehash, dataPtr.baseAddress, dataPtr.count)
CCryptoBoringSSL_MLDSA65_prehash_finalize(muPtr.baseAddress, &prehash)
return rc
}
}
}

guard rc == 1 else {
throw CryptoKitError.internalBoringSSLError()
}

return mu
}

/// The size of the public key in bytes.
static let byteCount = Int(MLDSA65_PUBLIC_KEY_BYTES)
}
Expand Down Expand Up @@ -386,6 +478,17 @@ extension MLDSA87 {
try self.backing.signature(for: data, context: context)
}

/// Generate a signature for the prehashed message representative (a.k.a. "external mu").
///
/// > Note: The message representative should be obtained via calls to ``MLDSA87/PublicKey/prehash(for:context:)``.
///
/// - Parameter mu: The prehashed message representative (a.k.a. "external mu").
///
/// - Returns: The signature of the prehashed message representative.
func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data {
try self.backing.signature(forPrehashedMessageRepresentative: mu)
}

/// The size of the private key in bytes.
static let byteCount = Backing.byteCount

Expand Down Expand Up @@ -486,6 +589,38 @@ extension MLDSA87 {
return signature
}

/// Generate a signature for the prehashed message representative (a.k.a. "external mu").
///
/// > Note: The message representative should be obtained via calls to ``MLDSA87/PublicKey/prehash(for:context:)``.
///
/// - Parameter mu: The prehashed message representative (a.k.a. "external mu").
///
/// - Returns: The signature of the prehashed message representative.
func signature(forPrehashedMessageRepresentative mu: some DataProtocol) throws -> Data {
guard mu.count == MLDSA.muByteCount else {
throw CryptoKitError.incorrectParameterSize
}

var signature = Data(repeating: 0, count: MLDSA87.signatureByteCount)

let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in
let muBytes: ContiguousBytes = mu.regions.count == 1 ? mu.regions.first! : Array(mu)
return muBytes.withUnsafeBytes { muPtr in
CCryptoBoringSSL_MLDSA87_sign_message_representative(
signaturePtr.baseAddress,
&self.key,
muPtr.baseAddress
)
}
}

guard rc == 1 else {
throw CryptoKitError.internalBoringSSLError()
}

return signature
}

/// The size of the private key in bytes.
static let byteCount = Int(MLDSA87_PRIVATE_KEY_BYTES)
}
Expand Down Expand Up @@ -544,6 +679,27 @@ extension MLDSA87 {
self.backing.isValidSignature(signature, for: data, context: context)
}

/// Generate a prehashed message representative (a.k.a. "external mu") for the given message.
///
/// - Parameter data: The message to prehash.
///
/// - Returns: The prehashed message representative (a.k.a. "external mu").
func prehash<D: DataProtocol>(for data: D) throws -> Data {
let context: Data? = nil
return try self.backing.prehash(for: data, context: context)
}

/// Generate a prehashed message representative (a.k.a. "external mu") for the given message.
///
/// - Parameters:
/// - data: The message to prehash.
/// - context: The context of the message.
///
/// - Returns: The prehashed message representative (a.k.a. "external mu").
func prehash<D: DataProtocol, C: DataProtocol>(for data: D, context: C) throws -> Data {
try self.backing.prehash(for: data, context: context)
}

/// The size of the public key in bytes.
static let byteCount = Backing.byteCount

Expand Down Expand Up @@ -625,6 +781,41 @@ extension MLDSA87 {
}
}

/// Generate a prehashed message representative (a.k.a. "external mu") for the given message.
///
/// - Parameters:
/// - data: The message to prehash.
/// - context: The context of the message.
///
/// - Returns: The prehashed message representative (a.k.a. "external mu").
func prehash<D: DataProtocol, C: DataProtocol>(for data: D, context: C?) throws -> Data {
var mu = Data(repeating: 0, count: MLDSA.muByteCount)

let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
let rc: CInt = mu.withUnsafeMutableBytes { muPtr in
dataBytes.withUnsafeBytes { dataPtr in
context.withUnsafeBytes { contextPtr in
var prehash = MLDSA87_prehash()
let rc = CCryptoBoringSSL_MLDSA87_prehash_init(
&prehash,
&key,
contextPtr.baseAddress,
contextPtr.count
)
CCryptoBoringSSL_MLDSA87_prehash_update(&prehash, dataPtr.baseAddress, dataPtr.count)
CCryptoBoringSSL_MLDSA87_prehash_finalize(muPtr.baseAddress, &prehash)
return rc
}
}
}

guard rc == 1 else {
throw CryptoKitError.internalBoringSSLError()
}

return mu
}

/// The size of the public key in bytes.
static let byteCount = Int(MLDSA87_PUBLIC_KEY_BYTES)
}
Expand All @@ -640,6 +831,9 @@ extension MLDSA87 {
enum MLDSA {
/// The size of the seed in bytes.
static let seedByteCount = 32

/// The size of the "mu" value in bytes.
fileprivate static let muByteCount = 64
}

#endif // CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
Loading