1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCrypto open source project
//
// Copyright (c) 2019-2020 Apple Inc. and the SwiftCrypto project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.md for the list of SwiftCrypto project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
@_exported import CryptoKit
#else
import Foundation
/// A container for hybrid public key encryption (HPKE) operations.
///
/// Hybrid public key encryption (HPKE) uses a symmetric encryption algorithm to encrypt data, and encapsulates the symmetric
/// encryption material using a public key encryption algorithm.
///
/// HPKE ensures that the ciphertext wasn't tampered with after its creation. It can also check the validity
/// of additional cleartext data in apps where you need to send headers or other metadata as cleartext.
///
/// HPKE optionally incorporates sender authentication, allowing the recipient to validate the authenticity of
/// messages using the sender's public key.
///
/// HPKE is described in the Internet Research Task Force (IRTF) document
/// [RFC 9180](https://www.ietf.org/rfc/rfc9180.pdf).
///
/// ## Topics
///
/// ### Sending and receiving messages
/// - ``Sender``
/// - ``Recipient``
///
/// ### Choosing cryptographic algorithms
/// - ``Ciphersuite``
/// - ``AEAD``
/// - ``KDF``
/// - ``KEM``
/// - ``DHKEM``
///
/// ### Handling errors
/// - ``Errors``
public enum HPKE {}
extension HPKE {
/// A type that represents the sending side of an HPKE message exchange.
///
/// To create encrypted messages, initialize a `Sender` specifying the appropriate cipher suite,
/// the recipient's public key, and the additional cryptographic material relevant to your chosen mode of operation.
/// Call ``seal(_:)`` or ``seal(_:authenticating:)`` on the `Sender` instance for each message
/// in turn to retrieve its ciphertext. The recipient of the messages needs to process them in the
/// same order as the `Sender`, using the same encryption mode, cipher suite, and key schedule information
/// (`info`), as well as the `Sender`'s ``encapsulatedKey``.
public struct Sender {
private var context: Context
/// The encapsulated symmetric key that the recipient uses to decrypt messages.
public let encapsulatedKey: Data
/// The exporter secret.
internal var exporterSecret: SymmetricKey {
return context.keySchedule.exporterSecret
}
/// Exports a secret given domain-separation context and the desired output length.
/// - Parameters:
/// - context: Application-specific information providing context on the use of this key.
/// - outputByteCount: The desired length of the exported secret.
/// - Returns: The exported secret.
public func exportSecret<Context: DataProtocol>(context: Context, outputByteCount: Int) throws -> SymmetricKey {
precondition(outputByteCount > 0);
return LabeledExpand(prk: self.exporterSecret,
label: Data("sec".utf8),
info: context,
outputByteCount: UInt16(outputByteCount),
suiteID: self.context.keySchedule.ciphersuite.identifier,
kdf: self.context.keySchedule.ciphersuite.kdf)
}
/// Creates a sender in base mode.
///
/// The `Sender` encrypts messages in base mode with a symmetric encryption key it derives using a key derivation function (KDF).
/// The KDF uses the key schedule data in `info` as input to generate the key.
/// The `Sender` encapsulates the derived key using the recipient's public key.
/// You access the encapsulated key using ``encapsulatedKey``.
///
/// - Parameters:
/// - recipientKey: The recipient's public key for encrypting the messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<PK: HPKEDiffieHellmanPublicKey>(recipientKey: PK, ciphersuite: Ciphersuite, info: Data) throws {
self.context = try Context(senderRoleWithCiphersuite: ciphersuite, mode: .base, psk: nil, pskID: nil, pkR: recipientKey, info: info)
self.encapsulatedKey = context.encapsulated
}
/// Creates a sender in preshared key (PSK) mode.
///
/// The `Sender` encrypts messages in PSK mode using a symmetric encryption key that the sender and recipient both know in advance, in combination with a key it derives using a key derivation function (KDF) and
/// the key schedule data in `info`.
/// The `Sender` encapsulates the derived key using the recipient's public key.
/// You access the encapsulated key using ``encapsulatedKey``.
///
/// - Parameters:
/// - recipientKey: The recipient's public key for encrypting the messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - psk: A preshared key (PSK) that the sender and the recipient both hold.
/// - pskID: An identifier for the PSK.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<PK: HPKEDiffieHellmanPublicKey>(recipientKey: PK, ciphersuite: Ciphersuite, info: Data, presharedKey psk: SymmetricKey, presharedKeyIdentifier pskID: Data) throws {
self.context = try Context(senderRoleWithCiphersuite: ciphersuite, mode: .psk, psk: psk, pskID: pskID, pkR: recipientKey, info: info)
self.encapsulatedKey = context.encapsulated
}
/// Creates a sender in authentication mode.
///
/// The `Sender` encrypts messages in authentication mode with a symmetric encryption key.
/// Messages also include authentication data so that the recipient can verify the authenticity of the sender’s private key.
///
/// - Parameters:
/// - recipientKey: The recipient's public key for encrypting the messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - authenticationKey: The sender's private key for generating the HMAC.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<SK: HPKEDiffieHellmanPrivateKey>(recipientKey: SK.PublicKey, ciphersuite: Ciphersuite, info: Data, authenticatedBy authenticationKey: SK) throws {
self.context = try Context(senderRoleWithCiphersuite: ciphersuite, mode: .auth, psk: nil, pskID: nil, pkR: recipientKey, info: info, skS: authenticationKey)
self.encapsulatedKey = context.encapsulated
}
/// Creates a sender in authentication and preshared key mode.
///
/// The `Sender` encrypts messages in authentication and preshared key (`auth_psk`) mode using
/// a symmetric encryption key that the sender and recipient both know in advance, in combination with a key it derives using a key derivation function (KDF) and
/// the key schedule data in `info`.
/// Messages also include authentication data so that the recipient can verify the authenticity of the sender’s private key.
///
/// - Parameters:
/// - recipientKey: The recipient's public key for encrypting the messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - authenticationKey: The sender's private key for generating the HMAC.
/// - psk: A preshared key (PSK) that the sender and the recipient both hold.
/// - pskID: An identifier for the PSK.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<SK: HPKEDiffieHellmanPrivateKey>(recipientKey: SK.PublicKey, ciphersuite: Ciphersuite, info: Data, authenticatedBy authenticationKey: SK, presharedKey psk: SymmetricKey, presharedKeyIdentifier pskID: Data) throws {
self.context = try Context(senderRoleWithCiphersuite: ciphersuite, mode: .auth_psk, psk: psk, pskID: pskID, pkR: recipientKey, info: info, skS: authenticationKey)
self.encapsulatedKey = context.encapsulated
}
/// Encrypts the given cleartext message and attaches additional authenticated data.
///
/// You can call this method multiple times to encrypt a series of messages.
/// When using this method, you need to supply ciphertext messages to the decryption
/// code on the receiving side in the same order as you encrypt them.
///
/// - Parameters:
/// - msg: The cleartext message to encrypt.
/// - aad: Additional data that the `Sender` authenticates and adds to the message in cleartext.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
/// - Returns: The ciphertext for the recipient to decrypt.
public mutating func seal<M: DataProtocol, AD: DataProtocol>(_ msg: M, authenticating aad: AD) throws -> Data {
return try context.keySchedule.seal(msg, authenticating: aad)
}
/// Encrypts the given cleartext message.
///
/// You can call this method multiple times to encrypt a series of messages.
/// When using this method, you need to supply ciphertext messages to the decryption
/// code on the receiving side in the same order as you encrypt them.
///
/// - Parameters:
/// - msg: The cleartext message to encrypt.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
/// - Returns: The ciphertext for the recipient to decrypt.
public mutating func seal<M: DataProtocol>(_ msg: M) throws -> Data {
return try context.keySchedule.seal(msg, authenticating: Data())
}
}
/// A type that represents the receiving side of an HPKE message exchange.
///
/// To decrypt and verify the identity of encrypted messages, initialize a `Recipient` specifying the appropriate
/// cipher suite, the receiver's private key, the encapsulated symmetric key, and the additional cryptographic
/// material relevant to your chosen mode of operation.
/// Call ``open(_:)`` or ``open(_:authenticating:)`` on the `Recipient` instance for each message
/// in turn to retrieve its cleartext. The recipient of the messages needs to process them in the
/// same order as the `Sender`, using the same cipher suite, encryption mode, and key schedule information
/// (`info` data).
/// Use a separate `Recipient` instance for each stream of messages.
public struct Recipient {
private var context: Context
/// The exporter secret.
internal var exporterSecret: SymmetricKey {
return context.keySchedule.exporterSecret
}
/// Exports a secret given domain-separation context and the desired output length.
/// - Parameters:
/// - context: Application-specific information providing context on the use of this key.
/// - outputByteCount: The desired length of the exported secret.
/// - Returns: The exported secret.
public func exportSecret<Context: DataProtocol>(context: Context, outputByteCount: Int) throws -> SymmetricKey {
precondition(outputByteCount > 0);
return LabeledExpand(prk: self.exporterSecret,
label: Data("sec".utf8),
info: context,
outputByteCount: UInt16(outputByteCount),
suiteID: self.context.keySchedule.ciphersuite.identifier,
kdf: self.context.keySchedule.ciphersuite.kdf)
}
/// Creates a recipient in base mode.
///
/// The `Receiver` decrypts messages in base mode using the encapsulated key with the key schedule information (`info` data).
///
/// - Parameters:
/// - privateKey: The recipient's private key for decrypting the incoming messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - encapsulatedKey: The encapsulated symmetric key that the sender provides.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<SK: HPKEDiffieHellmanPrivateKey>(privateKey: SK, ciphersuite: Ciphersuite, info: Data, encapsulatedKey: Data) throws {
self.context = try Context(recipientRoleWithCiphersuite: ciphersuite, mode: .base, enc: encapsulatedKey, psk: nil, pskID: nil, skR: privateKey, info: info, pkS: nil)
}
/// Creates a recipient in preshared key (PSK) mode.
///
/// The `Receiver` decrypts messages in PSK mode using the encapsulated key with the key schedule information (`info` data),
/// in addition to a symmetric encryption key that the sender and recipient both know in advance.
///
/// - Parameters:
/// - privateKey: The recipient's private key for decrypting the incoming messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - encapsulatedKey: The encapsulated symmetric key that the sender provides.
/// - psk: A preshared key (PSK) that the sender and the recipient both hold.
/// - pskID: An identifier for the PSK.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<SK: HPKEDiffieHellmanPrivateKey>(privateKey: SK, ciphersuite: Ciphersuite, info: Data, encapsulatedKey: Data, presharedKey psk: SymmetricKey, presharedKeyIdentifier pskID: Data) throws {
self.context = try Context(recipientRoleWithCiphersuite: ciphersuite, mode: .psk, enc: encapsulatedKey, psk: psk, pskID: pskID, skR: privateKey, info: info, pkS: nil)
}
/// Creates a recipient in authentication mode.
///
/// The `Receiver` decrypts messages in authentication mode using the encapsulated key with the key schedule information (`info` data).
/// Messages also include authentication data so that the recipient can verify the authenticity of the sender’s private key.
///
/// - Parameters:
/// - privateKey: The recipient's private key for decrypting the incoming messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - encapsulatedKey: The encapsulated symmetric key that the sender provides.
/// - authenticationKey: The sender's public key for authenticating the messages.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<SK: HPKEDiffieHellmanPrivateKey>(privateKey: SK, ciphersuite: Ciphersuite, info: Data, encapsulatedKey: Data, authenticatedBy authenticationKey: SK.PublicKey) throws {
self.context = try Context(recipientRoleWithCiphersuite: ciphersuite, mode: .auth, enc: encapsulatedKey, psk: nil, pskID: nil, skR: privateKey, info: info, pkS: authenticationKey)
}
/// Creates a recipient in authentication and preshared key mode.
///
/// The `Receiver` decrypts messages it receives in authentication and preshared key (`auth_psk`) mode
/// using the encapsulated key with the key schedule information (`info` data),
/// in addition to a symmetric encryption key that the sender and recipient both know in advance.
/// Messages also include authentication data so that the recipient can verify the authenticity of the sender’s private key.
///
/// - Parameters:
/// - privateKey: The recipient's private key for decrypting the incoming messages.
/// - ciphersuite: The cipher suite that defines the cryptographic algorithms to use.
/// - info: Data that the key derivation function uses to compute the symmetric key material. The sender and the recipient need to use the same `info` data.
/// - encapsulatedKey: The encapsulated symmetric key that the sender provides.
/// - authenticationKey: The sender's public key for authenticating the messages.
/// - psk: A preshared key (PSK) that the sender and the recipient both hold.
/// - pskID: An identifier for the PSK.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
public init<SK: HPKEDiffieHellmanPrivateKey>(privateKey: SK, ciphersuite: Ciphersuite, info: Data, encapsulatedKey: Data, authenticatedBy authenticationKey: SK.PublicKey, presharedKey psk: SymmetricKey, presharedKeyIdentifier pskID: Data) throws {
self.context = try Context(recipientRoleWithCiphersuite: ciphersuite, mode: .auth_psk, enc: encapsulatedKey, psk: psk, pskID: pskID, skR: privateKey, info: info, pkS: authenticationKey)
}
/// Decrypts a message, if the ciphertext is valid, verifying the integrity of additional authentication data.
///
/// You can call this method multiple times to decrypt a series of messages.
/// When using this method, the recipient of the ciphertext messages needs to decrypt
/// them in the same order that the sender encrypts them.
/// The system doesn't decrypt the additional authentication data in the `aad` parameter
/// that the recipient uses to verify the message integrity.
///
/// - Parameters:
/// - ciphertext: The ciphertext message to decrypt.
/// - aad: Additional cleartext data to authenticate.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
/// - Returns: The resulting cleartext message if the message is authentic.
public mutating func open<C: DataProtocol, AD: DataProtocol>(_ ciphertext: C, authenticating aad: AD) throws -> Data {
return try context.keySchedule.open(ciphertext, authenticating: aad)
}
/// Decrypts a message, if the ciphertext is valid.
///
/// You can call this method multiple times to decrypt a series of messages.
/// When using this method, the recipient of the ciphertext messages needs to decrypt
/// them in the same order that the sender encrypts them.
///
/// - Parameters:
/// - ciphertext: The ciphertext message to decrypt.
/// - Note: The system throws errors from ``CryptoKit/HPKE/Errors`` when it encounters them.
/// - Returns: The resulting cleartext message if the message is authentic.
public mutating func open<C: DataProtocol>(_ ciphertext: C) throws -> Data {
return try context.keySchedule.open(ciphertext, authenticating: Data())
}
}
}
#endif // Linux or !SwiftPM
|