It seems so much simpler to encrypt using java than obj-c with Tink. Given a known 32-bytes XChaCha20Poly1305 key, and a 16-bytes authenticated data (aad), how can the same be done in objective-c?
Java:
import com.google.crypto.tink.subtle.XChaCha20Poly1305;
XChaCha20Poly1305 xChaCha20Poly1305 = new XChaCha20Poly1305(key);
byte[] encryptedData = xChaCha20Poly1305.encrypt(plainData, aad);
Long story short, the following java and obj-c codes work together.
Java:
import com.google.crypto.tink.aead.AeadConfig;
import com.google.crypto.tink.*;
byte[] plainData = ...
byte[] key = ...//32 bytes
byte[] aad = ...
AeadConfig.register();
byte[] preffix = {0x1a,0x20};
byte[] fullKey = new byte[preffix.length + key.length];
System.arraycopy(preffix, 0, fullKey, 0, preffix.length);
System.arraycopy(key, 0, fullKey, preffix.length, key.length);
String fullKeyBase64 = new String(com.groups.network.aes.Base64.encode(fullKey), "UTF-8");
String jsonKey = "{\"primaryKeyId\":1635322858,\"key\":[{\"keyData\":{\"typeUrl\":\"type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key\",\"value\":\""+fullKeyBase64+"\",\"keyMaterialType\":\"SYMMETRIC\"},\"status\":\"ENABLED\",\"keyId\":1635322858,\"outputPrefixType\":\"TINK\"}]}";
KeysetHandle keysetHandle = CleartextKeysetHandle.read(
JsonKeysetReader.withString(jsonKey));
Aead aead = keysetHandle.getPrimitive(Aead.class);
byte[] encrypted = aead.encrypt(plainData, aad);
Objective C:
NSData* encryptedBytes = ...
NSData* aad = ...
NSData* key = ...//32-bytes
NSError *error = nil;
TINKAeadConfig *aeadConfig = [[TINKAeadConfig alloc] initWithError:&error];
if (!aeadConfig || error) {
//handle error
}
if (![TINKConfig registerConfig:aeadConfig error:&error]) {
//handle error
}
NSString* preffix = #"1a20";
NSMutableData* preffixData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for(i=0; i<[preffix length]/2;i++){
byte_chars[0] = [preffix characterAtIndex:i*2];
byte_chars[1] = [preffix characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[preffixData appendBytes:&whole_byte length:1];
}
NSData* originalKeyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* finalKey = [preffixData mutableCopy];
[finalKey appendData:originalKeyData];
NSString* jsonTmp = [NSString stringWithFormat:#"{\"primaryKeyId\":1635322858,\"key\":[{\"keyData\":{\"typeUrl\":\"type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key\",\"value\":\"%#\",\"keyMaterialType\":\"SYMMETRIC\"},\"status\":\"ENABLED\",\"keyId\":1635322858,\"outputPrefixType\":\"TINK\"}]}", [finalKey base64Encoding]];
NSData* jsonKeyData = [jsonTmp dataUsingEncoding:NSUTF8StringEncoding];
TINKJSONKeysetReader *reader = [[TINKJSONKeysetReader alloc] initWithSerializedKeyset:jsonKeyData error:&error];
if (!reader || error) {
//handle error
}
TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initCleartextKeysetHandleWithKeysetReader:reader error:&error];
if (!handle || error) {
//handle error
}
id<TINKAead> aead = [TINKAeadFactory primitiveWithKeysetHandle:handle error:&error];
if (!aead || error) {
//handle error
}
NSData *aadData = [aad dataUsingEncoding:NSUTF8StringEncoding];
NSData *plaintext = [aead decrypt:encryptedBytes withAdditionalData:aadData error:&error];
if (!plaintext || error) {
//handle error
}
Pay attention to the {0x1a,0x20}. This changes according to the type of encryption you use (here it's fine for XChacha20-Poly1305). This can be "discovered" with the Tinkey tool, which outputs a JSON key template if you run something like java -jar tinkey_deploy.jar create-keyset --key-template XCHACHA20_POLY1305 (the output base64 key needs to then be transformed to bytes and you'll need to figure which first 2 bytes correspond to which encryption)
Related
I am trying to stream AAC encoded audio data received as CMSampleBuffer. AudioConverterFillComplexBuffer returns 0 status code.
But after passing this data to my FFMPEG HLSWriter audio is not correctly saved (short truncated signals).
Below is the sample code.
static OSStatus inInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData)
{
KFAACEncoder *encoder = (__bridge KFAACEncoder *)(inUserData);
UInt32 requestedPackets = *ioNumberDataPackets;
if(requestedPackets > encoder.cycledBuffer.size / 2)
{
//NSLog(#"PCM buffer isn't full enough!");
*ioNumberDataPackets = 0;
return -1;
}
static size_t staticBuffSize = 4096;
static void* staticBuff = nil;
if(!staticBuff)
{
staticBuff = malloc(staticBuffSize);
}
size_t outputBytesSize = requestedPackets * 2;
[encoder.cycledBuffer popToBuffer:staticBuff bytes: outputBytesSize];
ioData->mBuffers[0].mData = staticBuff;
ioData->mBuffers[0].mDataByteSize = (int)outputBytesSize;
*ioNumberDataPackets = ioData->mBuffers[0].mDataByteSize / 2;
return noErr;
}
- (void) encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
CFRetain(sampleBuffer);
dispatch_async(self.encoderQueue,
^{
if (!_audioConverter)
{
[self setupAACEncoderFromSampleBuffer:sampleBuffer];
}
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CMTime pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
CFRetain(blockBuffer);
size_t pcmBufferSize = 0;
void* pcmBuffer = nil;
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &pcmBufferSize, &pcmBuffer);
[_cycledBuffer push:pcmBuffer size:pcmBufferSize];
NSError *error = nil;
if (status != kCMBlockBufferNoErr)
{
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
memset(_aacBuffer, 0, _aacBufferSize);
AudioBufferList outAudioBufferList = {0};
outAudioBufferList.mNumberBuffers = 1;
outAudioBufferList.mBuffers[0].mNumberChannels = 1;
outAudioBufferList.mBuffers[0].mDataByteSize = _aacBufferSize;
outAudioBufferList.mBuffers[0].mData = _aacBuffer;
AudioStreamPacketDescription *outPacketDescription = NULL;
UInt32 ioOutputDataPacketSize = 1;
status = AudioConverterFillComplexBuffer(_audioConverter,
inInputDataProc,
(__bridge void *)(self),
&ioOutputDataPacketSize,
&outAudioBufferList,
NULL);
NSData *data = nil;
if (status == 0)
{
NSData *rawAAC = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
if (_addADTSHeader) {
NSData *adtsHeader = [self adtsDataForPacketLength:rawAAC.length];
NSMutableData *fullData = [NSMutableData dataWithData:adtsHeader];
[fullData appendData:rawAAC];
data = fullData;
} else {
data = rawAAC;
}
} else {
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
}
if (self.delegate) {
KFFrame *frame = [[KFFrame alloc] initWithData:data pts:pts];
NSLog(#"Bytes of data %lu", (unsigned long)data.length);
dispatch_async(self.callbackQueue, ^{
[self.delegate encoder:self encodedFrame:frame];
});
}
CFRelease(sampleBuffer);
CFRelease(blockBuffer);
});
}
I create public key using SecKeyCreateWithData. Key is created from Pem after stripping the headers.
I have tried to verify signature using
SecKeyRawVerify returns -9809 error (iOS)
SecKeyVerifySignature returns -67808 "RSA signature verification failed, no match" (iOS)
SecTransformExecute returns false. (OSx)
We use SHA256 and Unicode encoding to sign the message(C# RSACryptoProvider).
Code for creating public key:
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #2048,
(id)kSecPublicKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag1
},
(id)kSecAttrCanEncrypt:#YES,
(id)kSecAttrCanVerify:#YES,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic
};
CFErrorRef error = NULL;
SecKeyRef keyRef = SecKeyCreateWithData((__bridge CFDataRef)publicKeyData,
(__bridge CFDictionaryRef)attributes,
&error);
Verification Code iOS:
size_t signedHashBytesSize = SecKeyGetBlockSize(keyRef);
const void* signedHashBytes = [signature bytes];
NSData *plainData = [dataToSign dataUsingEncoding:NSUTF16StringEncoding];
size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
uint8_t* hashBytes = malloc(hashBytesSize);
if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
return nil;
}
OSStatus status1 = SecKeyRawVerify(keyRef,
kSecPaddingPKCS1,
hashBytes,
hashBytesSize,
signedHashBytes,
signedHashBytesSize);
Verification Code OSx:
verifier = SecVerifyTransformCreate(keyRef, (__bridge CFDataRef)self.digest, &errorCF);
if (errorCF) { CFShow(errorCF);}
SecTransformSetAttribute(verifier,
kSecTransformInputAttributeName,
(__bridge CFTypeRef)plainData,
&errorCF);
if (errorCF) { CFShow(errorCF); exit(-1); }
SecTransformSetAttribute(verifier,
kSecDigestTypeAttribute,
kSecDigestSHA2,
&errorCF);
if (errorCF) { CFShow(errorCF); exit(-1); }
SecTransformSetAttribute(verifier,
kSecDigestLengthAttribute,
(__bridge CFNumberRef)#256,
&errorCF);
if (errorCF) { CFShow(errorCF); exit(-1); }
CFBooleanRef result1 = NULL;
result1 = SecTransformExecute(verifier, &errorCF);
BOOL success = (result1 != NULL);
I am stuck any help would be appreciated.
I've a private key file .p12.
I'm using Objective-C.
I need to generate Signed digital signature XML like this:
The problem is, I cannot find out how to generate the value in <ds:X509Certificate>. I know it is a public key base64 encoded. But, I can't get the same string after testing various encoding methods..
Need your help!
Found the solution myself. It is so easy.
Using Apple's security.framework:
NSString* pfile = [[NSBundle mainBundle] pathForResource:#"rsa_user" ofType:#"p12" ];
// Load Certificate
NSData *p12data = [NSData dataWithContentsOfFile:pfile];
CFDataRef inP12data = (__bridge CFDataRef)p12data;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
NSData *certificateData = (__bridge NSData *) SecCertificateCopyData(myCertificate);
// Output certificate base64 value. (This value goes to <ds:X509Certificate> tag )
NSLog(#"%#", [certificateData base64EncodedString]);
// Helper function
OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12data, options, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
*trust = (SecTrustRef)tempTrust;
}
if (options) {
CFRelease(options);
}
return securityError;
}
I am unable to unzip a zip file saved in Sqlite As Blob , after i query the db it returns me a NSData . I tried the inflate method but it returns this error "Decompression of data failed with code -3". Any opinions??
Thanks !
This is the code
NSData *content = [[NSData alloc] initWithBytes:sqlite3_column_blob(statement, 3) length:sqlite3_column_bytes(statement, 3)];
content = [NSData gzipInflate:content];
/// Ns data category
#implementation NSData (Unzip)
+ (NSData *)gzipInflate:(NSData*)data
{
if ([data length] == 0) return data;
unsigned full_length = [data length];
unsigned half_length = [data length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[data bytes];
strm.avail_in = [data length];
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
while (!done)
{
// Make sure we have enough room and reset the lengths.
if (strm.total_out >= [decompressed length])
[decompressed increaseLengthBy: half_length];
// strm.next_out = [decompressed mutableBytes] + strm.total_out;
strm.avail_out = [decompressed length] - strm.total_out;
// Inflate another chunk.
status = inflate (&strm, Z_SYNC_FLUSH);
if (status == Z_STREAM_END) done = YES;
else if (status != Z_OK) break;
}
if (inflateEnd (&strm) != Z_OK) return nil;
// Set real length.
if (done)
{
[decompressed setLength: strm.total_out];
return [NSData dataWithData: decompressed];
}
else return nil;
}
#end
I'm successfully sending int and CGPoint data in the struct of the GKTank example, BUT I tried desperately to send a NSString. I also tried it with char instead of NSString with no success...
Here's the code for
sending:
NSString *playerName = #"My Name";
NSData *dat = [playerName dataUsingEncoding:NSUTF8StringEncoding];
[BTINconnector sendNetworkPacketwithPacketID:NETWORK_PLAYERNAME withData:dat ofLength:sizeof(dat) reliable:YES];
the method (known from GKTank, modified)
- (void)sendNetworkPacketwithPacketID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend {
//NSLog(#"bmpc:ok 3 send packet to %i",gamePeerId);
// the packet we'll send is resued
static unsigned char networkPacket[kMaxPacketSize];
const unsigned int packetHeaderSize = 2 * sizeof(int); // we have two "ints" for our header
if(length < (kMaxPacketSize - packetHeaderSize)) { // our networkPacket buffer size minus the size of the header info
int *pIntData = (int *)&networkPacket[0];
// header info
pIntData[0] = gamePacketNumber++;
pIntData[1] = packetID;
// copy data in after the header
memcpy( &networkPacket[packetHeaderSize], data, length );
NSData *packet = [NSData dataWithBytes: networkPacket length: (length+8)];
if(howtosend == YES) {
[mySession sendData:packet toPeers:[NSArray arrayWithObject:gamePeerId] withDataMode:GKSendDataReliable error:nil];
} else {
[mySession sendData:packet toPeers:[NSArrayarrayWithObject:gamePeerId] withDataMode:GKSendDataUnreliable error:nil];
}
}
}
and the method for receiving (from GKTank modified by me)
- (void)receiveDataDG:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context {
static int lastPacketTime = -1;
unsigned char *incomingPacket = (unsigned char *)[data bytes];
int *pIntData = (int *)&incomingPacket[0];
NSData* nData = (NSData*)&incomingPacket[0];
//NSData* bData = nData[8];
//
// developer check the network time and make sure packers are in order
//
int packetTime = pIntData[0];
int packetID = pIntData[1];
if(packetTime < lastPacketTime && packetID != NETWORK_COINTOSS) {
NSLog(#"bmc: EXITED");
return;
}
lastPacketTime = packetTime;
switch( packetID ) {
case NETWORK_PLAYERNAME:
{
NSData *hjk = [NSData dataWithBytes:&pIntData[2] length:sizeof(int)];
NSString *gotitb = [[NSString alloc] initWithData:hjk encoding:NSUTF8StringEncoding];
NSLog(#"bmc:str %#,%#",gotitb,gotitb);
…
The gotitb returns null, but I don't know why. Please help.
You don't want sizeof(dat) you want [dat length].