Skip to content

Commit

Permalink
Secure Cell passphrase API: ObjCThemis (#609)
Browse files Browse the repository at this point in the history
* Passphrase API support in ObjCThemis

Add passphrase API support for Objective-C and Swift. The new private
subclass TSCellSealWithPassphrase implements encryption and decryption
with passphrases while TSCellSeal provides initializers.

This is not how you do class clusters in Objective-C, but unfortunately
this is the only way to get a sane Swift API without actually writing
Swift code. We cannot include Swift code in ObjCThemis because Swift
lacks ABI stability and Xcode projects require a particular version of
Swift to be set.

Swift 5 supports stable ABI but we cannot do much about this while we
have to support Swift 4 (which we will probably do until it is available
in Xcode).

Current implementation is usable as is but it is currently impossible
for users to inherit from TSCellSealWithPassphrase. This should not be
necessary, it seems, but there are legitimate cases when this might be
requested (i.e., common base64-encoded encryption). We'll deal with that
later based on user feedback. It is possible to export the private
subclass to allow subclassing it, but initialization via TSCellSeal is
still much more convenient.

* Remove the initWithPassphrase:usingEncoding: method

Since it's not possible to bridge this method into Swift, let's drop it
completely. Replace the test with the same as in Swift which checks that
we are able to decrypt data with UTF-16BE passphrase.
  • Loading branch information
ilammy committed Mar 27, 2020
1 parent 3ccfcd5 commit e1f14b3
Show file tree
Hide file tree
Showing 5 changed files with 600 additions and 4 deletions.
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
23 changes: 22 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,28 @@ 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 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
115 changes: 115 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,31 @@
#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
{
NSData *passphraseUTF8 = [passphrase dataUsingEncoding:NSUTF8StringEncoding];
return [self initWithPassphraseData:passphraseUTF8];
}

- (nullable instancetype)initWithPassphraseData:(NSData *)passphrase
{
// Avoid a warning about "Convenience initializer missing a 'self' call
// to another initializer" by assigning the new instance to self first.
self = [TSCellSealWithPassphrase alloc];
self = [self initWithPassphraseData:passphrase];
return self;
}

#pragma mark - Encryption

- (nullable NSData *)encrypt:(NSData *)message
Expand Down Expand Up @@ -161,3 +179,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 encryptedLength = 0;
res = themis_secure_cell_encrypt_seal_with_passphrase(self.key.bytes,
self.key.length,
context.bytes,
context.length,
data.bytes,
data.length,
NULL,
&encryptedLength);
if (res != THEMIS_BUFFER_TOO_SMALL) {
if (error) {
*error = SCERROR(res, @"Secure Cell encryption failed");
}
return nil;
}

NSMutableData *encryptedData = [NSMutableData dataWithLength:encryptedLength];

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

[encryptedData setLength:encryptedLength];
return encryptedData;
}

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

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

NSMutableData *decryptedData = [NSMutableData dataWithLength:decryptedLength];

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

[decryptedData setLength:decryptedLength];
return decryptedData;
}

@end
Loading

0 comments on commit e1f14b3

Please sign in to comment.