Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SLH-DSA post-quantum signatures to _CryptoExtras #278

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
140afbe
Include experimental primitives
fpseverino Apr 22, 2024
3b41fd7
Test implementation of SPHINCS+
fpseverino Apr 22, 2024
5f3d032
Update SPX implementation with more features and tests
fpseverino Apr 22, 2024
52d7f6c
Add publicKey computed from privateKey and SPX.Signature
fpseverino Apr 23, 2024
a416c1e
Make the SPX message conform to Digest or DataProtocol
fpseverino Apr 24, 2024
77a7f84
Add seed init for PublicKey
fpseverino May 5, 2024
365eda1
Add ASN1 support to SPX
fpseverino May 14, 2024
7925200
Set development flag to false
fpseverino May 15, 2024
3c44463
Added SPX test vectors
fpseverino May 21, 2024
aa0b274
Merge pull request #1 from apple/main
fpseverino May 21, 2024
1c25a05
Fix testing
fpseverino May 25, 2024
c530f73
Add constants for keys, seed and signature size
fpseverino Jun 14, 2024
ed0dac7
Merge pull request #3 from apple/main
fpseverino Aug 27, 2024
98f7db1
Merge pull request #5 from apple/main
fpseverino Oct 7, 2024
dca0e4e
Initial commit
fpseverino Oct 7, 2024
57c0ec8
Add some DocC
fpseverino Oct 8, 2024
8ef5ee5
Fix tests
fpseverino Oct 8, 2024
0369513
Remove unused test vectors
fpseverino Oct 8, 2024
643d07c
Add `Backing`
fpseverino Oct 8, 2024
25199fc
Properly encode and decode ASN1
fpseverino Oct 15, 2024
385bba7
Add more asserts to tests
fpseverino Oct 15, 2024
dd451f9
Add DocC
fpseverino Oct 16, 2024
af0edd9
Various improvements
fpseverino Oct 20, 2024
39b0b23
Remove ASN.1
fpseverino Oct 21, 2024
9f394a0
Fix documentation to clarify key size error messages and adjust raw r…
fpseverino Nov 16, 2024
2f05d99
Merge branch 'main' into slh-dsa
fpseverino Nov 20, 2024
dee8cae
Update CMake lists and license headers
fpseverino Nov 20, 2024
da39d26
Merge branch 'main' into slh-dsa
fpseverino Nov 22, 2024
571fd4b
Merge branch 'main' into slh-dsa
fpseverino Nov 27, 2024
a975178
Refactor `bytesCount`
fpseverino Nov 27, 2024
2e217ac
Try to remove unmodified file from git
fpseverino Nov 27, 2024
4a3718d
Merge branch 'main' into slh-dsa
fpseverino Nov 30, 2024
b6a93d6
Swift Format
fpseverino Nov 30, 2024
bf40b76
Add proper namespace for SHA2_128s parameter set
fpseverino Dec 12, 2024
7e89717
Swift Format
fpseverino Dec 12, 2024
e9d5a61
Merge branch 'main' into slh-dsa
fpseverino Dec 12, 2024
b6cfd8c
Merge branch 'main' into slh-dsa
fpseverino Dec 14, 2024
06d74f2
Merge branch 'main' into slh-dsa
fpseverino Dec 16, 2024
9f6d5b6
Merge branch 'main' into slh-dsa
fpseverino Dec 22, 2024
5750f5a
Add more testing
fpseverino Dec 22, 2024
a2fd7f7
Remove `Signature` struct
fpseverino Jan 3, 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
1 change: 1 addition & 0 deletions Sources/CCryptoBoringSSL/include/CCryptoBoringSSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "CCryptoBoringSSL_safestack.h"
#include "CCryptoBoringSSL_sha.h"
#include "CCryptoBoringSSL_siphash.h"
#include "CCryptoBoringSSL_slhdsa.h"
#include "CCryptoBoringSSL_trust_token.h"
#include "CCryptoBoringSSL_x509v3.h"

Expand Down
1 change: 1 addition & 0 deletions Sources/_CryptoExtras/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_library(_CryptoExtras
"RSA/RSA.swift"
"RSA/RSA_boring.swift"
"RSA/RSA_security.swift"
"SLHDSA/SLHDSA_boring.swift"
"Util/BoringSSLHelpers.swift"
"Util/CryptoKitErrors_boring.swift"
"Util/DigestType.swift"
Expand Down
293 changes: 293 additions & 0 deletions Sources/_CryptoExtras/SLHDSA/SLHDSA_boring.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCrypto open source project
//
// Copyright (c) 2024 Apple Inc. and the SwiftCrypto project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftCrypto project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

@_implementationOnly import CCryptoBoringSSL
import Crypto
import Foundation

/// A stateless hash-based digital signature algorithm that provides security against quantum computing attacks.
public enum SLHDSA {}

extension SLHDSA {
/// The SLH-DSA-SHA2-128s parameter set.
public enum SHA2_128s {}
}

extension SLHDSA.SHA2_128s {
/// A SLH-DSA-SHA2-128s private key.
public struct PrivateKey: Sendable {
private var backing: Backing

/// Initialize a SLH-DSA-SHA2-128s private key from a random seed.
public init() {
self.backing = Backing()
}

/// Initialize a SLH-DSA-SHA2-128s private key from a raw representation.
///
/// - Parameter rawRepresentation: The private key bytes.
///
/// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size.
public init(rawRepresentation: some DataProtocol) throws {
self.backing = try Backing(rawRepresentation: rawRepresentation)
}

/// The raw representation of the private key.
public var rawRepresentation: Data {
self.backing.rawRepresentation
}

/// The public key associated with this private key.
public var publicKey: PublicKey {
self.backing.publicKey
}

/// Generate a signature for the given data.
///
/// - Parameters:
/// - data: The message to sign.
/// - context: The context to use for the signature.
///
/// - Returns: The signature of the message.
public func signature<D: DataProtocol>(for data: D, context: D? = nil) throws -> Data {
try self.backing.signature(for: data, context: context)
}

fileprivate final class Backing {
private let pointer: UnsafeMutablePointer<UInt8>

func withUnsafePointer<T>(_ body: (UnsafePointer<UInt8>) throws -> T) rethrows -> T {
try body(self.pointer)
}

/// Initialize a SLH-DSA-SHA2-128s private key from a random seed.
init() {
self.pointer = UnsafeMutablePointer<UInt8>.allocate(
capacity: SLHDSA.SHA2_128s.PrivateKey.Backing.byteCount
)

withUnsafeTemporaryAllocation(
of: UInt8.self,
capacity: SLHDSA.SHA2_128s.PublicKey.Backing.byteCount
) { publicKeyPtr in
CCryptoBoringSSL_SLHDSA_SHA2_128S_generate_key(publicKeyPtr.baseAddress, self.pointer)
}
}

/// Initialize a SLH-DSA-SHA2-128s private key from a raw representation.
///
/// - Parameter rawRepresentation: The private key bytes.
///
/// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size.
init(rawRepresentation: some DataProtocol) throws {
guard rawRepresentation.count == SLHDSA.SHA2_128s.PrivateKey.Backing.byteCount else {
throw CryptoKitError.incorrectKeySize
}

self.pointer = UnsafeMutablePointer<UInt8>.allocate(
capacity: SLHDSA.SHA2_128s.PrivateKey.Backing.byteCount
)
self.pointer.initialize(
from: Array(rawRepresentation),
count: SLHDSA.SHA2_128s.PrivateKey.Backing.byteCount
)
}

/// The raw representation of the private key.
var rawRepresentation: Data {
Data(UnsafeBufferPointer(start: self.pointer, count: SLHDSA.SHA2_128s.PrivateKey.Backing.byteCount))
}

/// The public key associated with this private key.
var publicKey: PublicKey {
PublicKey(privateKeyBacking: self)
}

/// Generate a signature for the given data.
///
/// - Parameters:
/// - data: The message to sign.
/// - context: The context to use for the signature.
///
/// - Returns: The signature of the message.
func signature<D: DataProtocol>(for data: D, context: D? = nil) throws -> Data {
var signature = Data(repeating: 0, count: SLHDSA.SHA2_128s.signatureByteCount)

let rc: CInt = signature.withUnsafeMutableBytes { signaturePtr in
let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
return bytes.withUnsafeBytes { dataPtr in
if let context {
CCryptoBoringSSL_SLHDSA_SHA2_128S_sign(
signaturePtr.baseAddress,
self.pointer,
dataPtr.baseAddress,
dataPtr.count,
Array(context),
context.count
)
} else {
CCryptoBoringSSL_SLHDSA_SHA2_128S_sign(
signaturePtr.baseAddress,
self.pointer,
dataPtr.baseAddress,
dataPtr.count,
nil,
0
)
}
}
}

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

return signature
}

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

extension SLHDSA.SHA2_128s {
/// A SLH-DSA-SHA2-128s public key.
public struct PublicKey: Sendable {
private var backing: Backing

fileprivate init(privateKeyBacking: PrivateKey.Backing) {
self.backing = Backing(privateKeyBacking: privateKeyBacking)
}

/// Initialize a SLH-DSA-SHA2-128s public key from a raw representation.
///
/// - Parameter rawRepresentation: The public key bytes.
///
/// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size.
public init(rawRepresentation: some DataProtocol) throws {
self.backing = try Backing(rawRepresentation: rawRepresentation)
}

/// The raw representation of the public key.
public var rawRepresentation: Data {
self.backing.rawRepresentation
}

/// Verify a signature for the given data.
///
/// - Parameters:
/// - signature: The signature to verify.
/// - data: The message to verify the signature against.
/// - context: The context to use for the signature verification.
///
/// - Returns: `true` if the signature is valid, `false` otherwise.
public func isValidSignature<S: DataProtocol, D: DataProtocol>(
_ signature: S,
for data: D,
context: D? = nil
) -> Bool {
self.backing.isValidSignature(signature, for: data, context: context)
}

fileprivate final class Backing {
private let pointer: UnsafeMutablePointer<UInt8>

init(privateKeyBacking: PrivateKey.Backing) {
self.pointer = UnsafeMutablePointer<UInt8>.allocate(
capacity: SLHDSA.SHA2_128s.PublicKey.Backing.byteCount
)
privateKeyBacking.withUnsafePointer { privateKeyPtr in
CCryptoBoringSSL_SLHDSA_SHA2_128S_public_from_private(self.pointer, privateKeyPtr)
}
}

/// Initialize a SLH-DSA-SHA2-128s public key from a raw representation.
///
/// - Parameter rawRepresentation: The public key bytes.
///
/// - Throws: `CryptoKitError.incorrectKeySize` if the raw representation is not the correct size.
init(rawRepresentation: some DataProtocol) throws {
guard rawRepresentation.count == SLHDSA.SHA2_128s.PublicKey.Backing.byteCount else {
throw CryptoKitError.incorrectKeySize
}

self.pointer = UnsafeMutablePointer<UInt8>.allocate(
capacity: SLHDSA.SHA2_128s.PublicKey.Backing.byteCount
)
self.pointer.initialize(
from: Array(rawRepresentation),
count: SLHDSA.SHA2_128s.PublicKey.Backing.byteCount
)
}

/// The raw representation of the public key.
var rawRepresentation: Data {
Data(UnsafeBufferPointer(start: self.pointer, count: SLHDSA.SHA2_128s.PublicKey.Backing.byteCount))
}

/// Verify a signature for the given data.
///
/// - Parameters:
/// - signature: The signature to verify.
/// - data: The message to verify the signature against.
/// - context: The context to use for the signature verification.
///
/// - Returns: `true` if the signature is valid, `false` otherwise.
func isValidSignature<S: DataProtocol, D: DataProtocol>(
_ signature: S,
for data: D,
context: D? = nil
) -> Bool {
let signatureBytes: ContiguousBytes =
signature.regions.count == 1 ? signature.regions.first! : Array(signature)
return signatureBytes.withUnsafeBytes { signaturePtr in
let dataBytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
let rc: CInt = dataBytes.withUnsafeBytes { dataPtr in
if let context {
CCryptoBoringSSL_SLHDSA_SHA2_128S_verify(
signaturePtr.baseAddress,
signaturePtr.count,
self.pointer,
dataPtr.baseAddress,
dataPtr.count,
Array(context),
context.count
)
} else {
CCryptoBoringSSL_SLHDSA_SHA2_128S_verify(
signaturePtr.baseAddress,
signaturePtr.count,
self.pointer,
dataPtr.baseAddress,
dataPtr.count,
nil,
0
)
}
}
return rc == 1
}
}

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

extension SLHDSA.SHA2_128s {
/// The size of the signature in bytes.
private static let signatureByteCount = 7856
}
Loading