diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/AESEncryptor.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/AESEncryptor.swift index fbdac09..d309e1a 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/AESEncryptor.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/AESEncryptor.swift @@ -55,6 +55,7 @@ struct AESJWEEncryptor: JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R> { guard let alg = getKeyAlgorithm( diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/DirectEncrypter.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/DirectEncrypter.swift index 11de5e3..a2337e2 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/DirectEncrypter.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/DirectEncrypter.swift @@ -50,6 +50,7 @@ struct DirectJWEEncryptor: JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R> { guard let enc = getEncoding( diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDH1PUEncrypter.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDH1PUEncrypter.swift index 39ed172..115c515 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDH1PUEncrypter.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDH1PUEncrypter.swift @@ -59,6 +59,7 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R> { guard let alg = getKeyAlgorithm( @@ -100,11 +101,9 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor { throw JWE.JWEError.missingRecipientKey } - guard let ephemeralKeyPair = try getEphemeralKey( - protectedHeader: protectedHeader, - unprotectedHeader: unprotectedHeader, - recipientHeader: recipientHeader - ) ?? senderKey.keyGeneration?.generateKeyPairJWK(purpose: .keyAgreement) else { + guard let ephemeralKeyPair = try ephemeralKey ?? + senderKey.keyGeneration?.generateKeyPairJWK(purpose: .keyAgreement) + else { throw JWE.JWEError.missingEphemeralKey } @@ -144,7 +143,8 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor { recipientHeader: finalRecipientHeader, cek: cek, initializationVector: initializationVector, - additionalAuthenticationData: aad + additionalAuthenticationData: aad, + ephemeralKey: ephemeralKeyPair ) } @@ -160,7 +160,8 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor { recipientHeader: R?, cek: Data?, initializationVector: Data?, - additionalAuthenticationData: Data + additionalAuthenticationData: Data, + ephemeralKey: JWK ) throws -> JWEParts<P, R> { guard let alg = getKeyAlgorithm( protectedHeader: protectedHeader, @@ -239,7 +240,8 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor { encryptedKey: encryptedKey.encryptedKey, additionalAuthenticationData: additionalAuthenticationData, initializationVector: contentIv, - authenticationTag: encryptionResult.authenticationData + authenticationTag: encryptionResult.authenticationData, + ephemeralKey: ephemeralKey ) } else { let cek = try deriveSharedKey( @@ -294,7 +296,8 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor { encryptedKey: nil, additionalAuthenticationData: additionalAuthenticationData, initializationVector: contentIv, - authenticationTag: encryptionResult.authenticationData + authenticationTag: encryptionResult.authenticationData, + ephemeralKey: ephemeralKey ) } } diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDHEncrypter.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDHEncrypter.swift index e254a63..c87a8c3 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDHEncrypter.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/ECDHEncrypter.swift @@ -58,6 +58,7 @@ struct ECDHJWEEncryptor: JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R>{ guard let alg = getKeyAlgorithm( @@ -95,11 +96,9 @@ struct ECDHJWEEncryptor: JWEEncryptor { throw JWE.JWEError.missingRecipientKey } - guard let ephemeralKeyPair = try getEphemeralKey( - protectedHeader: protectedHeader, - unprotectedHeader: unprotectedHeader, - recipientHeader: recipientHeader - ) ?? recipientKey.keyGeneration?.generateKeyPairJWK(purpose: .keyAgreement) else { + guard let ephemeralKeyPair = try ephemeralKey + ?? recipientKey.keyGeneration?.generateKeyPairJWK(purpose: .keyAgreement) + else { throw JWE.JWEError.missingEphemeralKey } diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/JWEEncrypter.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/JWEEncrypter.swift index 8ef8a51..a058fd2 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/JWEEncrypter.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/JWEEncrypter.swift @@ -56,6 +56,7 @@ public protocol JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R> } @@ -130,6 +131,7 @@ extension JWEEncryptor { password: Data? = nil, saltLength: Int? = nil, iterationCount: Int? = nil, + ephemeralKey: JWK? = nil, multiRecipients: Bool = false ) throws -> JWEParts<P, R> { try self.encrypt( @@ -145,6 +147,7 @@ extension JWEEncryptor { password: password, saltLength: saltLength, iterationCount: iterationCount, + ephemeralKey: ephemeralKey, hasMultiRecipients: multiRecipients ) } diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/MultiEncryptor.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/MultiEncryptor.swift index 7f0872e..9446ca6 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/MultiEncryptor.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/MultiEncryptor.swift @@ -37,6 +37,9 @@ struct MultiEncryptor: JWEMultiEncryptor { iterationCount: Int?, encryptionModule: JWEEncryptionModule = .default ) throws -> [JWEParts<P, R>] { + guard !recipients.isEmpty else { + throw JWE.JWEError.noRecipients + } guard let enc = getEncoding( protectedHeader: protectedHeader, unprotectedHeader: unprotectedHeader, @@ -70,6 +73,7 @@ struct MultiEncryptor: JWEMultiEncryptor { password: password, saltLength: saltLength, iterationCount: iterationCount, + ephemeralKey: nil, hasMultiRecipients: true ) @@ -95,6 +99,7 @@ struct MultiEncryptor: JWEMultiEncryptor { password: password, saltLength: saltLength, iterationCount: iterationCount, + ephemeralKey: firstEncryption.ephemeralKey, hasMultiRecipients: true ) } diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/PasswordBasedEncrypter.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/PasswordBasedEncrypter.swift index 1713cb9..63095ab 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/PasswordBasedEncrypter.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/PasswordBasedEncrypter.swift @@ -56,6 +56,7 @@ struct PasswordBasedJWEEncryptor: JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R> { let iterationCount = getSaltCount( diff --git a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/RSAEncrypter.swift b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/RSAEncrypter.swift index 272a6bc..d62682d 100644 --- a/Sources/JSONWebEncryption/EncryptionModule/Encryptors/RSAEncrypter.swift +++ b/Sources/JSONWebEncryption/EncryptionModule/Encryptors/RSAEncrypter.swift @@ -52,6 +52,7 @@ struct RSAJWEEncryptor: JWEEncryptor { password: Data?, saltLength: Int?, iterationCount: Int?, + ephemeralKey: JWK?, hasMultiRecipients: Bool ) throws -> JWEParts<P, R> { guard let alg = getKeyAlgorithm( diff --git a/Sources/JSONWebEncryption/JWEParts.swift b/Sources/JSONWebEncryption/JWEParts.swift index 7657196..745f3d4 100644 --- a/Sources/JSONWebEncryption/JWEParts.swift +++ b/Sources/JSONWebEncryption/JWEParts.swift @@ -15,6 +15,7 @@ */ import Foundation +import JSONWebKey // `JWEParts` represents the constituent parts of a JSON Web Encryption (JWE) object. /// It's a generic struct that can accommodate different types of headers for both protected and recipient-specific data. @@ -23,25 +24,28 @@ import Foundation /// - R: A type conforming to `JWERegisteredFieldsHeader` used for the recipient-specific header. public struct JWEParts<P: JWERegisteredFieldsHeader, R: JWERegisteredFieldsHeader> { /// The protected header, containing shared information about the encryption. - let protectedHeader: P? + public var protectedHeader: P? /// The recipient-specific header, potentially containing information tailored for the individual recipient. - let recipientHeader: R? + public var recipientHeader: R? /// The ciphertext, which is the encrypted content. - let cipherText: Data + public let cipherText: Data /// The encrypted key, used to decrypt the content. - let encryptedKey: Data? + public let encryptedKey: Data? /// Additional authenticated data, if any, used in the encryption process. - let additionalAuthenticationData: Data? + public let additionalAuthenticationData: Data? /// The initialization vector used in the encryption process, for algorithms that require it. - let initializationVector: Data? + public let initializationVector: Data? /// The authentication tag, verifying the integrity and authenticity of the encrypted content. - let authenticationTag: Data? + public let authenticationTag: Data? + + /// To ensure on cases of multiple encryption the ephemeral key is fully passed + let ephemeralKey: JWK? /// Initializes a new `JWEParts` instance with the specified components. /// - Parameters: @@ -52,7 +56,7 @@ public struct JWEParts<P: JWERegisteredFieldsHeader, R: JWERegisteredFieldsHeade /// - additionalAuthenticationData: Optional additional data authenticated along with the payload. /// - initializationVector: Optional initialization vector for certain encryption algorithms. /// - authenticationTag: Optional authentication tag for verifying integrity and authenticity. - init( + public init( protectedHeader: P?, recipientHeader: R?, cipherText: Data, @@ -60,6 +64,28 @@ public struct JWEParts<P: JWERegisteredFieldsHeader, R: JWERegisteredFieldsHeade additionalAuthenticationData: Data?, initializationVector: Data?, authenticationTag: Data? + ) { + self.init( + protectedHeader: protectedHeader, + recipientHeader: recipientHeader, + cipherText: cipherText, + encryptedKey: encryptedKey, + additionalAuthenticationData: additionalAuthenticationData, + initializationVector: initializationVector, + authenticationTag: authenticationTag, + ephemeralKey: nil + ) + } + + init( + protectedHeader: P?, + recipientHeader: R?, + cipherText: Data, + encryptedKey: Data?, + additionalAuthenticationData: Data?, + initializationVector: Data?, + authenticationTag: Data?, + ephemeralKey: JWK? ) { self.protectedHeader = protectedHeader self.recipientHeader = recipientHeader @@ -68,5 +94,6 @@ public struct JWEParts<P: JWERegisteredFieldsHeader, R: JWERegisteredFieldsHeade self.initializationVector = initializationVector self.cipherText = cipherText self.authenticationTag = authenticationTag + self.ephemeralKey = ephemeralKey } } diff --git a/Tests/JWETests/PBES2Tests.swift b/Tests/JWETests/PBES2Tests.swift index 7c0dd8e..727809e 100644 --- a/Tests/JWETests/PBES2Tests.swift +++ b/Tests/JWETests/PBES2Tests.swift @@ -40,6 +40,7 @@ final class PBES2Tests: XCTestCase { password: password, saltLength: 16, iterationCount: 8192, + ephemeralKey: nil, hasMultiRecipients: false ) @@ -81,6 +82,7 @@ final class PBES2Tests: XCTestCase { password: password, saltLength: 16, iterationCount: 8192, + ephemeralKey: nil, hasMultiRecipients: false ) @@ -122,6 +124,7 @@ final class PBES2Tests: XCTestCase { password: password, saltLength: 16, iterationCount: 8192, + ephemeralKey: nil, hasMultiRecipients: false )