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 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 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
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 @@ -34,6 +34,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
300 changes: 300 additions & 0 deletions Sources/_CryptoExtras/SLHDSA/SLHDSA_boring.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import Crypto
import Foundation

@_implementationOnly import CCryptoBoringSSL

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

extension SLHDSA {
/// 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 -> Signature {
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.PrivateKey.Backing.bytesCount)

withUnsafeTemporaryAllocation(of: UInt8.self, capacity: SLHDSA.PublicKey.Backing.bytesCount) { 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.PrivateKey.Backing.bytesCount else {
throw CryptoKitError.incorrectKeySize
}

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

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

/// 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 -> Signature {
let output = try Array<UInt8>(unsafeUninitializedCapacity: Signature.bytesCount) { bufferPtr, length in
let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
let result = bytes.withUnsafeBytes { dataPtr in
if let context {
CCryptoBoringSSL_SLHDSA_SHA2_128S_sign(
bufferPtr.baseAddress,
self.pointer,
dataPtr.baseAddress,
dataPtr.count,
Array(context),
context.count
)
} else {
CCryptoBoringSSL_SLHDSA_SHA2_128S_sign(
bufferPtr.baseAddress,
self.pointer,
dataPtr.baseAddress,
dataPtr.count,
nil,
0
)
}
}

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

length = Signature.bytesCount
}
return Signature(signatureBytes: output)
}

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

extension SLHDSA {
/// 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<D: DataProtocol>(_ signature: Signature, for data: D, context: D? = nil) -> Bool {
self.backing.isValidSignature(signature, for: data, context: context)
}

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

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

init(privateKeyBacking: PrivateKey.Backing) {
self.pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: SLHDSA.PublicKey.bytesCount)
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.PublicKey.bytesCount else {
throw CryptoKitError.incorrectKeySize
}

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


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

/// 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<D: DataProtocol>(_ signature: Signature, for data: D, context: D? = nil) -> Bool {
signature.withUnsafeBytes { signaturePtr in
let bytes: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
let rc: CInt = bytes.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 bytesCount = 32
}
}
}

extension SLHDSA {
/// A SLH-DSA-SHA2-128s signature.
public struct Signature: Sendable, ContiguousBytes {
/// The raw binary representation of the signature.
public var rawRepresentation: Data

/// Initialize a SLH-DSA-SHA2-128s signature from a raw representation.
///
/// - Parameter rawRepresentation: The signature bytes.
public init(rawRepresentation: some DataProtocol) {
self.rawRepresentation = Data(rawRepresentation)
}

/// Initialize a SLH-DSA-SHA2-128s signature from a raw representation.
///
/// - Parameter signatureBytes: The signature bytes.
init(signatureBytes: [UInt8]) {
self.rawRepresentation = Data(signatureBytes)
}

/// Access the signature bytes.
///
/// - Parameter body: The closure to execute with the signature bytes.
///
/// - Returns: The result of the closure.
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try self.rawRepresentation.withUnsafeBytes(body)
}

/// The size of the signature in bytes.
fileprivate static let bytesCount = 7856
}
}
2 changes: 1 addition & 1 deletion Sources/_CryptoExtras/Util/SubjectPublicKeyInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,4 @@ extension SubjectPublicKeyInfo {

return serializer.serializedBytes
}
}
}
Loading