From b2aa25e40cd4c96a6cdc614717a214584e042a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Frade?= <106541307+beatt83@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:45:34 +0000 Subject: [PATCH] fix: on multi encryption master ephemeral key ecdh1pu and ecdhes (#6) Also fixes a bug for encrypting without any recipients. --- .../Encryptors/AESEncryptor.swift | 1 + .../Encryptors/DirectEncrypter.swift | 1 + .../Encryptors/ECDH1PUEncrypter.swift | 21 +++++---- .../Encryptors/ECDHEncrypter.swift | 9 ++-- .../Encryptors/JWEEncrypter.swift | 3 ++ .../Encryptors/MultiEncryptor.swift | 5 +++ .../Encryptors/PasswordBasedEncrypter.swift | 1 + .../Encryptors/RSAEncrypter.swift | 1 + Sources/JSONWebEncryption/JWEParts.swift | 43 +++++++++++++++---- Tests/JWETests/PBES2Tests.swift | 3 ++ 10 files changed, 66 insertions(+), 22 deletions(-) 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 { 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 { 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 { 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 { 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{ 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 } @@ -130,6 +131,7 @@ extension JWEEncryptor { password: Data? = nil, saltLength: Int? = nil, iterationCount: Int? = nil, + ephemeralKey: JWK? = nil, multiRecipients: Bool = false ) throws -> JWEParts { 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] { + 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 { 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 { 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 { /// 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