Skip to content
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

Add obfuscation on Enum type and Enum elements #80

Merged
merged 3 commits into from
Jan 13, 2020

Conversation

hwdavr
Copy link
Contributor

@hwdavr hwdavr commented Jan 2, 2020

For this PR, it adds the support for enum type and enum elements.

@rockbruno
Copy link
Owner

Hey hwdavr! While enum obfuscation works, the reason we don't do it is because some enum elements aren't meant to be obfuscated (like CodingKeys)... The project will build, but it will break parsing of these properties.

The same reason for instance properties since changing their names would break Codable classes 😢

https://github.com/rockbruno/swiftshield/blob/master/SOURCEKITISSUES.md#types-that-wont-be-obfuscated

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 2, 2020

I agree that case like CodingKeys, shouldn't be obfuscated, but some enum type not used for CodingKeys still worth to obfuscate. Maybe we should has some options to filter out certain classes, like ProGuard in Android.

@rockbruno
Copy link
Owner

I don't remember the full context of why I haven't done this, but I think it was very hard to automatically determine if an enum was used as the CodingKeys.

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 3, 2020

I know it's difficult to determine automatically, but when we use Proguard in Android, it also has the similar issue. In Proguard, you have to keep those enum classes if you are going to serialise them.
Something like this,
-keep enum com.test.constant.APP_STATUS {
public *;
}
You also can use wildcast to keep the whole set of classes.
-keep enum com.google.**$Type { public protected *; }

PPiOS-Rename also provides a option '-F' to filter out some classes.

I think it's a common practice to exclude those Codable classes, rather than keeping everything even not related to Codable.

@rockbruno
Copy link
Owner

rockbruno commented Jan 3, 2020

Hmmm we could automatically ignore everything that has CodingKeys in the name, as I think I've never seen someone use a different naming convention for it :) With that, we probably wouldn't need an argument for ignoring additional names and the issue would be pretty much solved

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 4, 2020

CodingKeys is only used when they what to choose different name from the property name, what if they don't implement CodingKeys at all, just use default names.

And how do you prevent from obfuscating struct extending from Codable?

@rockbruno
Copy link
Owner

I think there are two cases:

1 - When the compiler synthesizes the Codable implementation
2 - When you do it manually

We don't need to worry about 1 because the lack of a declaration will prevent us from obfuscating it.

For 2, there will be an explicit CodingKeys declaration which we shouldn't obfuscate. Since I'm not sure if we can easily determine if the enum inherits from CodingKeys, we could just grab the name and determine from there (like not obfuscating ProfileCodingKeys because it has the CodingKeys suffix. I believe this is something everyone does so it should be safe)

I think this covers structs that extend from Codable, is there something else that comes to your mind?

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 5, 2020

Let's have one example to make sure my understanding is correct.
So what you are saying, the properties inside Message will not be obfuscated, because we don't obfuscate struct element. And we can filter out AttachmentCodingKeys, because it has CodingKeys suffix.
For the name of struct Message and Attachment, even we obfuscate them, it's not affecting the encoding and decoding?

struct Message: Codable {
  let from: String
  let text: String
  let attachments: [Attachment]
}

struct Attachment {
  let type: String
  let payload: Any?
  
  private enum AttachmentCodingKeys: String, CodingKey {
    case type
    case payload
  }
}

@rockbruno
Copy link
Owner

That's right, the name of the struct itself is irrelevant. We would need to recognize the struct itself if we obfuscated properties, which we dont because of other SourceKit bugs.

@rockbruno
Copy link
Owner

Apologies, we don't obfuscate properties precisely because of Codable — if we can recognize that a struct inherits from Codable, then we can activate these as well. But in this case of the enum the name of the type doesn't matter, yes.

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 5, 2020

OK, I will filter out enum with ending with CodingKeys.
But in the future, if we want to obfuscate properties, I think it's better to use additional option to filter certain classes.

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 7, 2020

Hi @rockbruno, I've commit the change for filtering out CodingKeys, please check.


if type == .enum && name.hasSuffix("CodingKeys") {
obfuscationData.excludedEnums.insert(usr)
return
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do this when we're filling the declarations data, otherwise if the enumelement gets referenced before the declaration they are going to be wrongly obfuscated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I see. I will update it.

// Enum element belongs to excluded enum
if usr.hasPrefix(exclusion) {
return
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Iterating every single excluded enum sounds unnecessary, since we're checking for a prefix can't we just remove the suffix and check if the remainder exists in the set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know the mangled name rules for Swift.
For the below example of enum element "firstName", I need to make sure I can remove the suffix "9firstNameyA2FmF" from "s:14SwiftShieldSDK6PersonC17_personCodingKeys33_7242533BA8890D95B0EE7BCC963FFFCBLLO9firstNameyA2FmF" correctly, but from what I know, the mangled name rules keeping change for different Swift version.

Found declaration of _personCodingKeys (s:14SwiftShieldSDK6PersonC17_personCodingKeys33_7242533BA8890D95B0EE7BCC963FFFCBLLO)
Found declaration of firstName (s:14SwiftShieldSDK6PersonC17_personCodingKeys33_7242533BA8890D95B0EE7BCC963FFFCBLLO9firstNameyA2FmF)
Found declaration of lastName (s:14SwiftShieldSDK6PersonC17_personCodingKeys33_7242533BA8890D95B0EE7BCC963FFFCBLLO8lastNameyA2FmF)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping that the number that comes before the name of the element was going to be constant... In that case, you could strip everything after the name of the enumelement

Let's try stripping everything after the last underline... I suppose that's harder to change but yeah, we might have problems with other Swift versions indeed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just commit a new change based on your idea.

let kind = data.kind

if let type = self.sourceKit.referenceType(kind: kind), type == .enum && name.hasSuffix("CodingKeys") {
obfuscationData.excludedEnums.insert(self.usrWithoutSuffix(usr))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good now, but I'm just wondering if we need to do self.usrWithoutSuffix(usr) here. The enum usr is already the stripped one, isn't it?

@hwdavr
Copy link
Contributor Author

hwdavr commented Jan 10, 2020

No, the enum class also has the a random hex string in the end, check this "s:14SwiftShieldSDK6PersonC17_personCodingKeys33_7242533BA8890D95B0EE7BCC963FFFCBLLO".

@rockbruno
Copy link
Owner

Thanks!

@rockbruno rockbruno merged commit 7cc28fd into rockbruno:master Jan 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants