-
Notifications
You must be signed in to change notification settings - Fork 116
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
[87] Implement direct encryption and symmetric JWKs #92
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @daniel-mohemian 👏 Looks really good! Found some typos and have 1-2 questions.
@@ -0,0 +1,34 @@ | |||
// | |||
// DataRSAPublicKey.swift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DataSymmetricKey
@@ -24,17 +24,17 @@ | |||
import Foundation | |||
import Security | |||
|
|||
internal enum SecureRandomError: Error { | |||
public enum SecureRandomError: Error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
JOSESwift/Sources/RSADecrypter.swift
Outdated
|
||
func decrypt(_ ciphertext: Data) throws -> Data { | ||
guard let privateKey = privateKey else { | ||
return Data() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome 👌 Couldn't remember why we returned plain Data()
. Maybe write a comment that we need a empty octet for direct encryption and therefore Data()
is returned. What do you think?
@@ -0,0 +1,78 @@ | |||
// | |||
// RSAKeyCodable.swift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SymmetricKeyCodable
@@ -0,0 +1,120 @@ | |||
// | |||
// RSAKeys.swift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SymmetricKeys
Tests/SymmetricKeyTests.swift
Outdated
|
||
class SymmetricKeyTests: XCTestCase { | ||
|
||
func testCreatingSymetricKeyFromData() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: testCreatingSymmetricKeyFromData
Tests/SymmetricKeyTests.swift
Outdated
let json = try! SymmetricKey( | ||
key: key, | ||
additionalParameters: [ "alg": SymmetricKeyAlgorithm.A256CBCHS512.rawValue ] | ||
).jsonData()! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting.
Tests/SymmetricKeyTests.swift
Outdated
} | ||
|
||
func testDecodingFromJSONWithMissingKeyType() { | ||
let json = "{\"alg\":\"A256CBC-HS512\",\"k\":\"+++==notbase64url==---\"}".data(using: .utf8)! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use a correct key otherwise you don't know if it throws because of the missing keytype or the not base64URL key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
Tests/SymmetricKeyTests.swift
Outdated
} | ||
|
||
func testDecodingFromJSONWithWrongKeyType() { | ||
let json = "{\"kty\":\"RSA\",\"alg\":\"A256CBC-HS512\",\"k\":\"+++==notbase64url==---\"}".data(using: .utf8)! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
} | ||
|
||
func testParsingSymmetricKeyFromOtherKeyRepresentation() { | ||
let key: ExpressibleAsSymmetricKeyComponents = Data(bytes: [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this different to the above test? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first tests initializing the key directly from Data
. The second from something that is ExpressibleAsSymmetricKeyComponents
. Currently, only Data
conforms to that protocol so it's basically the same, but uses two different initializers.
If you compare it to the RSA keys, there SecKey
and Data
are ExpressibleAsRSAPublicKeyComponents
but the key can only be initialized directly from modulus/exponent as String
s.
# Conflicts: # JOSESwift/Sources/CryptoImplementation/RSA.swift
@carol-mohemian Thanks for the feedback! I addressed all your comments (I hope 😄). |
Nice 👍 Would love to see a review by @gigi-mohemian as well. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome job @daniel-mohemian 👏
I have some very minor comments, none of them critical in any way.
/// - key: The key used to perform the decryption. If the `keyDecryptionAlgorithm` is `.direct`, the | ||
/// `decryptionKey` is the shared symmetric content encryption key. Otherwise the `decryptionKey` is the | ||
/// private key of the receiver. See [RFC-7516](https://tools.ietf.org/html/rfc7516#section-5.2) for | ||
/// details. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
switch (keyDecryptionAlgorithm, contentDecryptionAlgorithm) { | ||
case (.RSA1_5, .A256CBCHS512): | ||
guard type(of: kdk) is RSADecrypter.KeyType.Type else { | ||
guard type(of: key) is RSADecrypter.KeyType.Type else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this also be a guard let ... as?
so that we don't need the force-cast below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a very good point. I did some testing and yes, normally that would work:
func printType<T>(of key: T) {
if let _ = key as? Data {
print("Data")
}
}
printType(of: Data()) // prints "Data"
However, doing the same with SecKey
(if let _ = key as? SecKey
) gives the following error:
error: conditional downcast to CoreFoundation type 'SecKey' will always succeed
After more testing, this seems to be the situation:
- For normal Swift types, we could do optional let bindings as you suggested
- The current check
type(of: key) is SecKey
will succeed for all CoreFoundation types (SecKey, CFString, ...) but not for Swift types.
So if one passes Data()
as RSA key it will detect the wrong type, but if one passes e.g. a CFString, it won't detect the error. The correct way of checking CoreFoundation types would be to compare CFGetTypeID(key as CFTypeRef
to stuff like SecKeyGetTypeID()
. However, I currently don't know how we should solve that without knowing what *GetTypeID()
function to call (since the KeyType
is generic and CFGetTypeID()
only seems to work on AnyObject
s and not on types itself).
Since that's a bug not directly introduced by this pull request and it does not affect direct encryption, are you guys ok with fixing it separately? If yes, I'll track an issue. cc @carol-mohemian, @gigi-mohemian
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the thorough explanation!
Fixing this separately as you suggested is the way to go I'd say 👍
self.symmetric = AESDecrypter(algorithm: contentDecryptionAlgorithm) | ||
case (.direct, .A256CBCHS512): | ||
guard type(of: key) is AESDecrypter.KeyType.Type else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here: could this be a guard let ... as?
?
Also two more times in Encrypter.swift
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above ☝️
JOSESwift/Sources/JWK.swift
Outdated
@@ -41,6 +41,7 @@ internal enum JWKError: Error { | |||
/// - RSA | |||
public enum JWKKeyType: String, Codable { | |||
case RSA = "RSA" | |||
case SYM = "oct" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
|
||
keys.append(rsaKey) | ||
throw DecodingError.dataCorruptedError(in: keyContainer, debugDescription: """ | ||
No RSAPrivateKey, RSAPublicKey, or SymmetricKey found to decode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
JWKParameter.keyType, | ||
DecodingError.Context.init( | ||
codingPath: [JWKParameter.keyType], | ||
debugDescription: "Key Type parameter wrong." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would opt for "Wrong parameter: key type" - but the above is okay as well 👍
The title case of Key Type
is what caught my eye.
/// - key: The octet sequence containing the key data. | ||
/// - parameters: Additional JWK parameters. | ||
public init(key: Data, additionalParameters parameters: [String: String] = [:]) { | ||
self.keyType = .SYM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure about the naming here? While I don't actually disagree with the naming I was confused for a short while because SYM = "oct"
🤔
What do you think about going full on Symmetric
so it's not confused with an abbreviation or using OCT
directly? Just thinking out aloud here 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I agree. 👍
I changed it to .OCT
. (Nimbus also uses OCT
btw.)
4, 122, 29, 230, 151, 12, 244, 127, | ||
121, 25, 4, 85, 220, 144, 215, 110, | ||
130, 17, 68, 228, 129, 138, 7, 130 | ||
]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome formatting 👆
👌
Thx @gigi-mohemian for the review. |
* master: [87] Implement direct encryption and symmetric JWKs (#92) # Conflicts: # .gitignore # Tests/JWERSATests.swift
Resolves #87.
This implements direct encryption for JWEs as well as symmetric JWKs.
Usage
This is a made up example to show off all new functionality.
API Changes
The main
Encrypter
andDecrypter
initializers changed fromand from
respectively.
The old initializers are still available (altough marked as deprecated), so we can release this as a minor version bump with no breaking API changes.
Regarding the weird Xcode file: Apparently it should be commited.