Skip to content

Commit

Permalink
fix: on multi encryption master ephemeral key ecdh1pu and ecdhes (#6)
Browse files Browse the repository at this point in the history
Also fixes a bug for encrypting without any recipients.
  • Loading branch information
beatt83 authored Feb 6, 2024
1 parent 3984add commit b2aa25e
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -144,7 +143,8 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor {
recipientHeader: finalRecipientHeader,
cek: cek,
initializationVector: initializationVector,
additionalAuthenticationData: aad
additionalAuthenticationData: aad,
ephemeralKey: ephemeralKeyPair
)
}

Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -294,7 +296,8 @@ struct ECDH1PUJWEEncryptor: JWEEncryptor {
encryptedKey: nil,
additionalAuthenticationData: additionalAuthenticationData,
initializationVector: contentIv,
authenticationTag: encryptionResult.authenticationData
authenticationTag: encryptionResult.authenticationData,
ephemeralKey: ephemeralKey
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public protocol JWEEncryptor {
password: Data?,
saltLength: Int?,
iterationCount: Int?,
ephemeralKey: JWK?,
hasMultiRecipients: Bool
) throws -> JWEParts<P, R>
}
Expand Down Expand Up @@ -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(
Expand All @@ -145,6 +147,7 @@ extension JWEEncryptor {
password: password,
saltLength: saltLength,
iterationCount: iterationCount,
ephemeralKey: ephemeralKey,
hasMultiRecipients: multiRecipients
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -70,6 +73,7 @@ struct MultiEncryptor: JWEMultiEncryptor {
password: password,
saltLength: saltLength,
iterationCount: iterationCount,
ephemeralKey: nil,
hasMultiRecipients: true
)

Expand All @@ -95,6 +99,7 @@ struct MultiEncryptor: JWEMultiEncryptor {
password: password,
saltLength: saltLength,
iterationCount: iterationCount,
ephemeralKey: firstEncryption.ephemeralKey,
hasMultiRecipients: true
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct PasswordBasedJWEEncryptor: JWEEncryptor {
password: Data?,
saltLength: Int?,
iterationCount: Int?,
ephemeralKey: JWK?,
hasMultiRecipients: Bool
) throws -> JWEParts<P, R> {
let iterationCount = getSaltCount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
43 changes: 35 additions & 8 deletions Sources/JSONWebEncryption/JWEParts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -52,14 +56,36 @@ 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,
encryptedKey: Data?,
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
Expand All @@ -68,5 +94,6 @@ public struct JWEParts<P: JWERegisteredFieldsHeader, R: JWERegisteredFieldsHeade
self.initializationVector = initializationVector
self.cipherText = cipherText
self.authenticationTag = authenticationTag
self.ephemeralKey = ephemeralKey
}
}
3 changes: 3 additions & 0 deletions Tests/JWETests/PBES2Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ final class PBES2Tests: XCTestCase {
password: password,
saltLength: 16,
iterationCount: 8192,
ephemeralKey: nil,
hasMultiRecipients: false
)

Expand Down Expand Up @@ -81,6 +82,7 @@ final class PBES2Tests: XCTestCase {
password: password,
saltLength: 16,
iterationCount: 8192,
ephemeralKey: nil,
hasMultiRecipients: false
)

Expand Down Expand Up @@ -122,6 +124,7 @@ final class PBES2Tests: XCTestCase {
password: password,
saltLength: 16,
iterationCount: 8192,
ephemeralKey: nil,
hasMultiRecipients: false
)

Expand Down

0 comments on commit b2aa25e

Please sign in to comment.