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

Secure Cell passphrase API: ObjCThemis #609

Merged
merged 5 commits into from
Mar 27, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,38 @@ _Code:_

- New function `TSGenerateSymmetricKey()` (available in Objective-C and Swift) can be used to generate symmetric keys for Secure Cell ([#561](https://github.com/cossacklabs/themis/pull/561)).

- Secure Cell API updates ([#606](https://github.com/cossacklabs/themis/pull/606)).
- Secure Cell API updates:

- New encryption/decryption API with consistent naming: `encrypt` and `decrypt`.
- New encryption/decryption API with consistent naming: `encrypt` and `decrypt` ([#606](https://github.com/cossacklabs/themis/pull/606)).

- Improved Token Protect API:
- Improved Token Protect API ([#606](https://github.com/cossacklabs/themis/pull/606)):
- Encryption results use `NSData` now which bridges with Swift `Data` directly.
- Decryption no longer requires an intermediate `TSCellTokenEncryptedData` object.

- ObjCThemis now supports _passphrase API_ of in Seal mode ([#609](https://github.com/cossacklabs/themis/pull/609)).

In Swift:

```swift
let cell = TSCellSeal(passphrase: "secret")

let encrypted = try cell.encrypt("message".data(using: .utf8)!)
let decrypted = try cell.decrypt(encrypted)
```

In Objective-C:

```objective-c
TSCellSeal *cell = [[TSCellSeal alloc] initWithPassphrase:@"secret"];

NSData *encrypted = [cell encrypt:[@"message" dataUsingEncoding:NSUTF8StringEncoding]];
NSData *decrypted = [cell decrypt:encrypted];
```

You can safely and securely use human-readable passphrases as strings with this new API.

Existing master key API (`TSCellSeal(key: ...)` or `initWithKey:...`) is not secure when used with passphrases. You should only use it with symmetric encryption keys, such as generated by `TSGenerateSymmetricKey()` ([#561](https://github.com/cossacklabs/themis/pull/561)).

- **Deprecated API**

- Secure Cell wrapData/unwrapData renamed into encrypt/decrypt ([#606](https://github.com/cossacklabs/themis/pull/606)).
Expand Down
39 changes: 38 additions & 1 deletion src/wrappers/themis/Obj-C/objcthemis/scell_seal.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,44 @@ NS_ASSUME_NONNULL_BEGIN
*
* @returns @c nil if key is empty.
*/
- (nullable instancetype)initWithKey:(NSData *)key;
- (nullable instancetype)initWithKey:(NSData *)key
NS_DESIGNATED_INITIALIZER;

/**
* Initialise Secure Cell in Seal mode with a passphrase.
*
* @param [in] passphrase non-empty passphrase to use
*
* The passphrase string will be encoded in UTF-8.
*
* @returns @c nil if passphrase is empty.
*/
- (nullable instancetype)initWithPassphrase:(NSString *)passphrase;

/**
* Initialise Secure Cell in Seal mode with a passphrase.
*
* @param [in] passphrase non-empty passphrase
* @param [in] encoding passphrase encoding
*
* @returns @c nil if passphrase is empty or cannot be encoded without data loss.
*/
- (nullable instancetype)initWithPassphrase:(NSString *)passphrase
usingEncoding:(NSStringEncoding)encoding
// NSStringEncoding translates into UInt in Swift which is not correct.
// We cannot use NS_REFINED_FOR_SWIFT because we cannot ship Swift code
// in our framework without ABI stability guarantees. Sorry, Swift users,
// but you'll have to encode your passphrases explicitly for now.
NS_SWIFT_UNAVAILABLE("cannot export for Swift directly");
Copy link
Contributor

Choose a reason for hiding this comment

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

lol poor users :(

Copy link
Contributor

Choose a reason for hiding this comment

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

but won't it confuse to have this interface in objc, but not to have in swift? maybe we should remove it completely, as most users use swift anyway?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Dunno... Normally, the users would not want to use this interface anyway, even in Objective-C, and if they really need to use a different encoding, they can always use the one that takes NSData. So by removing this interface we do not remove any features, only lower usability.

On the other hand, once we are able to ship Swift code with ObjCThemis (I imagine this should be possible when Swift 4 becomes obsolete), we can forward this interface to Swift with an extension. So we can leave it in Objective-C with this in mind, if this interface is useful.

“Help me, @Lagovas. You're my only hope.” Please be a tie-breaker.

The default decisions will be to remove it as we can extend the API in a future version, but we won't be able to remove it once it's published.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that removing this interface will lead to lower usability, but I do think that having interface-that-you-can't-use leads to frustration :)

My vote is to kill it 🔥

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

kill it 🔥

Done and done.


/**
* Initialise Secure Cell in Seal mode with raw passphrase data.
*
* @param [in] passphrase non-empty passphrase to use
*
* @returns @c nil if passphrase is empty.
*/
- (nullable instancetype)initWithPassphraseData:(NSData *)passphrase;

/**
* Encrypt data.
Expand Down
122 changes: 122 additions & 0 deletions src/wrappers/themis/Obj-C/objcthemis/scell_seal.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,38 @@
#import <objcthemis/serror.h>
#import <themis/themis.h>

@interface TSCellSealWithPassphrase : TSCellSeal
@end

@implementation TSCellSeal

- (nullable instancetype)initWithKey:(NSData *)key {
self = [super initWithKey:key];
return self;
}

- (nullable instancetype)initWithPassphrase:(NSString *)passphrase
{
return [self initWithPassphrase:passphrase usingEncoding:NSUTF8StringEncoding];
}

- (nullable instancetype)initWithPassphrase:(NSString *)passphrase
usingEncoding:(NSStringEncoding)encoding
{
NSData *encoded = [passphrase dataUsingEncoding:encoding];
if (!encoded) {
return nil;
}
return [self initWithPassphraseData:encoded];
}

- (nullable instancetype)initWithPassphraseData:(NSData *)passphrase
{
self = [TSCellSealWithPassphrase alloc];
Copy link
Contributor

Choose a reason for hiding this comment

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

why we do this as two lines?

self = [[TSCellSealWithPassphrase alloc] initWithPassphraseData:passphrase]];

won't it work like this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Uh... I forgot why. Just tested it, it seems I did this to suppress Xcode warning about the initializer not calling an init method on "self". With this code

self = [[TSCellSealWithPassphrase alloc] initWithPassphraseData:passphrase]];

the newly allocated object is not self (yet) so it triggers the warnings. Writing it like that keeps Xcode satisfied.

Should I add a comment in the code about that?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes please put a code comment.

something deeply triggers me here, but i don't really understand why and i don't remember all the specifics of ObjC coding 🤔

self = [self initWithPassphraseData:passphrase];
return self;
}

#pragma mark - Encryption

- (nullable NSData *)encrypt:(NSData *)message
Expand Down Expand Up @@ -161,3 +186,100 @@ - (nullable NSData *)unwrapData:(NSData *)message error:(NSError **)error
}

@end

@implementation TSCellSealWithPassphrase

- (nullable instancetype)initWithPassphraseData:(NSData *)passphrase
{
// Call grandparent TSCell initializer. We store the passphrase as a "key".
self = [super initWithKey:passphrase];
return self;
}

- (nullable NSData *)encrypt:(NSData *)data
context:(nullable NSData *)context
error:(NSError **)error
{
themis_status_t res = THEMIS_FAIL;

size_t encryptedLengh = 0;
res = themis_secure_cell_encrypt_seal_with_passphrase(self.key.bytes,
self.key.length,
context.bytes,
context.length,
data.bytes,
data.length,
NULL,
&encryptedLengh);
if (res != THEMIS_BUFFER_TOO_SMALL) {
if (error) {
*error = SCERROR(res, @"Secure Cell encryption failed");
}
return nil;
}

NSMutableData *encryptedData = [NSMutableData dataWithLength:encryptedLengh];

res = themis_secure_cell_encrypt_seal_with_passphrase(self.key.bytes,
self.key.length,
context.bytes,
context.length,
data.bytes,
data.length,
encryptedData.mutableBytes,
&encryptedLengh);
if (res != THEMIS_SUCCESS) {
if (error) {
*error = SCERROR(res, @"Secure Cell encryption failed");
}
return nil;
}

[encryptedData setLength:encryptedLengh];
return encryptedData;
}

- (nullable NSData *)decrypt:(NSData *)data
context:(nullable NSData *)context
error:(NSError **)error
{
themis_status_t res = THEMIS_FAIL;

size_t decryptedLengh = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
size_t decryptedLengh = 0;
size_t decryptedLenght = 0;

and below

Copy link
Contributor

Choose a reason for hiding this comment

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

(you see, i'm actually reading your PRs)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i'm actually reading your PRs

Awww... uwu

patting Korbo

-decryptedLenght
+decryptedLength

res = themis_secure_cell_decrypt_seal_with_passphrase(self.key.bytes,
self.key.length,
context.bytes,
context.length,
data.bytes,
data.length,
NULL,
&decryptedLengh);
if (res != THEMIS_BUFFER_TOO_SMALL) {
if (error) {
*error = SCERROR(res, @"Secure Cell decryption failed");
}
return nil;
}

NSMutableData *decryptedData = [NSMutableData dataWithLength:decryptedLengh];

res = themis_secure_cell_decrypt_seal_with_passphrase(self.key.bytes,
self.key.length,
context.bytes,
context.length,
data.bytes,
data.length,
decryptedData.mutableBytes,
&decryptedLengh);
if (res != THEMIS_SUCCESS) {
if (error) {
*error = SCERROR(res, @"Secure Cell decryption failed");
}
return nil;
}

[decryptedData setLength:decryptedLengh];
return decryptedData;
}

@end
Loading