2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "SecOTRSession.h"
27 #include "SecOTRMath.h"
28 #include "SecOTRIdentityPriv.h"
29 #include "SecOTRSessionPriv.h"
30 #include "SecOTRPackets.h"
31 #include "SecOTRPacketData.h"
32 #include "SecOTRDHKey.h"
34 #include <utilities/SecCFWrappers.h>
36 #include <CoreFoundation/CFRuntime.h>
37 #include <CoreFoundation/CFString.h>
39 #include <Security/SecBase.h>
40 #include <Security/SecRandom.h>
42 #include <AssertMacros.h>
44 #include <corecrypto/cchmac.h>
45 #include <corecrypto/ccsha2.h>
47 #include <os/activity.h>
51 static void SecOTRInitMyDHKeys(SecOTRSessionRef session
)
54 CFReleaseNull(session
->_myKey
);
55 session
->_myKey
= SecOTRFullDHKCreate(kCFAllocatorDefault
);
56 CFReleaseNull(session
->_myNextKey
);
57 session
->_myNextKey
= SecOTRFullDHKCreate(kCFAllocatorDefault
);
59 session
->_missedAck
= true;
60 session
->_receivedAck
= false;
61 bzero(session
->_keyCache
, sizeof(session
->_keyCache
));
63 secnotice("otr", "%@ Reinitializing DH Keys, first: %@", session
, session
->_myKey
);
66 OSStatus
SecOTRSAppendStartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
)
68 __block OSStatus result
= errSecSuccess
;
70 dispatch_sync(session
->_queue
, ^{
71 session
->_state
= kAwaitingDHKey
;
73 // Generate r and x and calculate gx:
74 SecOTRInitMyDHKeys(session
);
76 CFMutableDataRef dhMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
78 result
= SecRandomCopyBytes(kSecRandomDefault
, sizeof(session
->_r
), session
->_r
);
79 if (result
== errSecSuccess
) {
80 SecOTRAppendDHMessage(session
, dhMessage
);
83 CFDataPerformWithHexString(dhMessage
, ^(CFStringRef messageString
) {
84 secnotice("otr", "%@ Start packet: %@", session
, messageString
);
87 if (session
->_textOutput
) {
88 SecOTRPrepareOutgoingBytes(dhMessage
, appendPacket
);
90 CFDataAppend(appendPacket
, dhMessage
);
93 CFReleaseSafe(dhMessage
);
100 OSStatus
SecOTRSAppendRestartPacket(SecOTRSessionRef session
, CFMutableDataRef appendPacket
)
102 __block OSStatus result
= errSecSuccess
;
104 dispatch_sync(session
->_queue
, ^{
105 if (!session
->_myKey
) {
106 secerror("_myKey is NULL, avoiding crash");
107 result
= errSecDecode
;
110 CFMutableDataRef dhMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
112 session
->_state
= kAwaitingDHKey
;
113 CFReleaseNull(session
->_receivedDHMessage
);
114 CFReleaseNull(session
->_receivedDHKeyMessage
);
116 SecOTRAppendDHMessage(session
, dhMessage
);
118 CFDataPerformWithHexString(dhMessage
, ^(CFStringRef messageString
) {
119 secnotice("otr", "%@ Restart packet: %@", session
, messageString
);
122 if (session
->_textOutput
) {
123 SecOTRPrepareOutgoingBytes(dhMessage
, appendPacket
);
125 CFDataAppend(appendPacket
, dhMessage
);
127 CFReleaseSafe(dhMessage
);
133 static const uint8_t* FindGXHash(CFDataRef dhPacket
)
135 const uint8_t* messageBytes
= CFDataGetBytePtr(dhPacket
);
136 size_t remainingBytes
= (size_t)CFDataGetLength(dhPacket
);
138 OTRMessageType messageType
;
140 require_noerr(ReadHeader(&messageBytes
, &remainingBytes
, &messageType
), fail
);
141 require(messageType
== kDHMessage
, fail
);
143 uint32_t egxiLength
= 0;
144 require_noerr(ReadLong(&messageBytes
, &remainingBytes
, & egxiLength
), fail
);
145 require(egxiLength
<= remainingBytes
, fail
);
146 messageBytes
+= egxiLength
;
147 remainingBytes
-= egxiLength
;
149 uint32_t dataLength
= 0;
150 require_noerr(ReadLong(&messageBytes
, &remainingBytes
, &dataLength
), fail
);
151 require(dataLength
<= remainingBytes
, fail
);
152 require(dataLength
== CCSHA256_OUTPUT_SIZE
, fail
);
160 static bool SecOTRMyGXHashIsBigger(SecOTRSessionRef session
, CFDataRef dhCommitMessage
)
162 bool mineIsBigger
= false;
164 CFMutableDataRef myDHCommitMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
166 SecOTRAppendDHMessage(session
, myDHCommitMessage
);
168 const uint8_t* myHash
= FindGXHash(myDHCommitMessage
);
169 const uint8_t* theirHash
= FindGXHash(dhCommitMessage
);
171 require(myHash
, fail
);
172 require(theirHash
, fail
);
174 mineIsBigger
= 0 < memcmp(myHash
, theirHash
, CCSHA256_OUTPUT_SIZE
);
176 BufferPerformWithHexString(myHash
, CCSHA256_OUTPUT_SIZE
, ^(CFStringRef myHashString
) {
177 BufferPerformWithHexString(theirHash
, CCSHA256_OUTPUT_SIZE
, ^(CFStringRef theirHashString
) {
178 secdebug("otr", "%@ %s gx is bigger, M:%@ T:%@", session
, mineIsBigger
? "mine" : "their", myHashString
, theirHashString
);
183 CFReleaseNull(myDHCommitMessage
);
187 static OSStatus
SecOTRSProcessDHMessage(SecOTRSessionRef session
,
188 CFDataRef incomingPacket
,
189 CFMutableDataRef negotiationResponse
)
191 OSStatus result
= errSecParam
;
193 CFStringRef messageMessage
= CFSTR("");
195 switch (session
->_state
) {
197 // Compare hash values.
198 if (SecOTRMyGXHashIsBigger(session
, incomingPacket
)) {
199 // If we're bigger we resend to force them to deal.
200 messageMessage
= CFSTR("Our GX is bigger, resending DH");
201 CFReleaseNull(session
->_receivedDHMessage
);
202 SecOTRAppendDHMessage(session
, negotiationResponse
);
203 result
= errSecSuccess
;
205 } // Else intentionally fall through to idle
206 messageMessage
= CFSTR("Our GX is bigger, resending DH");
207 case kAwaitingSignature
:
210 // Generate a new X and GX..
211 SecOTRInitMyDHKeys(session
);
212 // If we were already waiting on reveal, then just send the packet again
213 case kAwaitingRevealSignature
:
214 SecOTRAppendDHKeyMessage(session
, negotiationResponse
);
216 if (messageMessage
== 0)
217 messageMessage
= CFSTR("Sending DHKey");
218 // Keep the packet for use later.
219 CFReleaseNull(session
->_receivedDHMessage
);
220 session
->_receivedDHMessage
= CFDataCreateCopy(kCFAllocatorDefault
, incomingPacket
);
222 session
->_state
= kAwaitingRevealSignature
;
223 result
= errSecSuccess
;
226 result
= errSecInteractionNotAllowed
;
230 if (result
== errSecSuccess
) {
231 CFDataPerformWithHexString(negotiationResponse
, ^(CFStringRef responseString
) {
232 secnotice("otr", "%@ %@: %@", session
, messageMessage
, responseString
);
235 secnotice("otr", "%@ Process DH failed %d", session
, (int)result
);
240 static OSStatus
SecOTRSetupTheirKeyFrom(SecOTRSessionRef session
, const uint8_t**data
, size_t*size
)
242 SecOTRPublicDHKeyRef tempKey
= SecOTRPublicDHKCreateFromSerialization(kCFAllocatorDefault
, data
, size
);
243 require(tempKey
!= NULL
, fail
);
245 return SecOTRSetupInitialRemoteKey(session
, tempKey
);
251 static OSStatus
SecOTRSExtractTheirPublicDHKey(SecOTRSessionRef session
, CFDataRef dhPacket
)
253 OSStatus result
= errSecParam
;
255 const uint8_t *messageBytes
= CFDataGetBytePtr(dhPacket
);
256 size_t messageSize
= (size_t)CFDataGetLength(dhPacket
);
257 OTRMessageType messageType
= kDHMessage
; // Suppress warning.
259 ReadHeader(&messageBytes
, &messageSize
, &messageType
);
260 require(messageType
== kDHKeyMessage
, exit
);
262 result
= SecOTRSetupTheirKeyFrom(session
, &messageBytes
, &messageSize
);
269 static OSStatus
SecOTRSProcessDHKeyMessage(SecOTRSessionRef session
,
270 CFDataRef incomingPacket
,
271 CFMutableDataRef negotiationResponse
)
273 OSStatus result
= errSecUnimplemented
;
274 CFStringRef messageMessage
= CFSTR("");
276 result
= SecOTRSExtractTheirPublicDHKey(session
, incomingPacket
);
277 require_noerr(result
, exit
);
279 switch (session
->_state
) {
281 CFReleaseNull(session
->_receivedDHKeyMessage
);
282 SecOTRAppendRevealSignatureMessage(session
, negotiationResponse
);
283 session
->_state
= kAwaitingSignature
;
284 session
->_receivedDHKeyMessage
= CFDataCreateCopy(kCFAllocatorDefault
, incomingPacket
);
285 result
= errSecSuccess
;
286 messageMessage
= CFSTR("Sending reveal signature");
288 case kAwaitingSignature
:
289 if (CFEqualSafe(incomingPacket
, session
->_receivedDHKeyMessage
)) {
290 SecOTRAppendRevealSignatureMessage(session
, negotiationResponse
);
291 messageMessage
= CFSTR("Resending reveal signature");
293 messageMessage
= CFSTR("Ignoring new DHKey message");
295 result
= errSecSuccess
;
299 case kAwaitingRevealSignature
:
300 result
= errSecSuccess
;
301 messageMessage
= CFSTR("Ignoring DHKey message");
304 result
= errSecInteractionNotAllowed
;
309 if (result
== errSecSuccess
) {
310 CFDataPerformWithHexString(negotiationResponse
, ^(CFStringRef responseString
) {
311 secnotice("otr", "%@ %@: %@", session
, messageMessage
, responseString
);
314 secnotice("otr", "%@ Process DH failed %d", session
, (int)result
);
321 static OSStatus
SecOTRSExtractR(SecOTRSessionRef session
,
322 const uint8_t **messageBytes
,
325 OSStatus result
= errSecDecode
;
327 OTRMessageType messageType
= kDHMessage
; // Suppress warning
329 ReadHeader(messageBytes
, messageSize
, &messageType
);
330 require(messageType
== kRevealSignatureMessage
, exit
);
334 ReadLong(messageBytes
, messageSize
, &rSize
);
335 require(rSize
== kOTRAuthKeyBytes
, exit
);
338 memcpy(session
->_r
, *messageBytes
, kOTRAuthKeyBytes
);
340 *messageBytes
+= kOTRAuthKeyBytes
;
341 *messageSize
-= kOTRAuthKeyBytes
;
343 result
= errSecSuccess
;
348 static OSStatus
FindEncGYInDHPacket(SecOTRSessionRef session
,
349 const uint8_t **dhMessageBytesPtr
,
350 size_t *messageSizePtr
,
351 size_t* encGYBufferSize
)
353 OSStatus result
= errSecParam
;
354 require_action(*encGYBufferSize
>= kExponentiationBytes
+ 4, exit
, result
= errSecParam
);
356 OTRMessageType messageType
;
357 result
= ReadHeader(dhMessageBytesPtr
, messageSizePtr
, &messageType
);
358 require_noerr(result
, exit
);
359 require_action(messageType
== kDHMessage
, exit
, result
= errSecDecode
);
361 uint32_t readEncSize
;
362 result
= ReadLong(dhMessageBytesPtr
, messageSizePtr
, &readEncSize
);
363 require_noerr(result
, exit
);
365 *encGYBufferSize
= readEncSize
;
367 // Don't bother erasing the public gy decrypted, it's public after all.
372 static OSStatus
SecOTRSExtractRAndTheirDHKey(SecOTRSessionRef session
,
373 const uint8_t **messageBytes
,
376 OSStatus result
= errSecDecode
;
378 require(session
->_receivedDHMessage
!= NULL
, exit
);
379 result
= SecOTRSExtractR(session
, messageBytes
, messageSize
);
380 require_noerr(result
, exit
);
382 uint8_t gxiDecrypted
[kExponentiationBytes
+ 4];
383 const uint8_t *gxiDecryptedBuffer
= gxiDecrypted
;
385 const uint8_t* dhMessageBytes
= CFDataGetBytePtr(session
->_receivedDHMessage
);
386 size_t dhMessageSize
= (size_t)CFDataGetLength(session
->_receivedDHMessage
);
388 size_t encGYSize
= sizeof(gxiDecrypted
);
389 result
= FindEncGYInDHPacket(session
, &dhMessageBytes
, &dhMessageSize
, &encGYSize
);
390 require_noerr(result
, exit
);
391 require_action(encGYSize
<= kExponentiationBytes
+ 4, exit
, result
= errSecDecode
);
393 AES_CTR_IV0_Transform(sizeof(session
->_r
), session
->_r
, encGYSize
, dhMessageBytes
, gxiDecrypted
);
395 result
= SecOTRSetupTheirKeyFrom(session
, &gxiDecryptedBuffer
, &encGYSize
);
398 // Don't bother erasing the public gy decrypted, it's public after all.
402 static OSStatus
SecVerifySignatureAndMac(SecOTRSessionRef session
,
404 const uint8_t **signatureAndMacBytes
,
405 size_t *signatureAndMacSize
)
407 OSStatus result
= errSecDecode
;
409 uint8_t m1
[kOTRAuthMACKeyBytes
];
410 uint8_t m2
[kOTRAuthMACKeyBytes
];
411 uint8_t c
[kOTRAuthKeyBytes
];
414 cc_unit s
[kExponentiationUnits
];
416 SecPDHKeyGenerateS(session
->_myKey
, session
->_theirKey
, s
);
417 // Derive M1, M2 and C, either prime or normal versions.
418 DeriveOTR256BitsFromS(usePrimes
? kM1Prime
: kM1
,
419 kExponentiationUnits
, s
, sizeof(m1
), m1
);
420 DeriveOTR256BitsFromS(usePrimes
? kM2Prime
: kM2
,
421 kExponentiationUnits
, s
, sizeof(m2
), m2
);
422 DeriveOTR128BitPairFromS(kCs
,
423 kExponentiationUnits
, s
,
424 sizeof(c
),usePrimes
? NULL
: c
,
425 sizeof(c
), usePrimes
? c
: NULL
);
429 cchmac_di_decl(ccsha256_di(), mBContext
);
431 cchmac_init(ccsha256_di(), mBContext
, sizeof(m1
), m1
);
434 CFMutableDataRef toHash
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
436 SecPDHKAppendSerialization(session
->_theirKey
, toHash
);
437 SecFDHKAppendPublicSerialization(session
->_myKey
, toHash
);
439 cchmac_update(ccsha256_di(), mBContext
, (size_t)CFDataGetLength(toHash
), CFDataGetBytePtr(toHash
));
441 CFReleaseNull(toHash
);
444 const uint8_t* encSigDataBlobStart
= *signatureAndMacBytes
;
447 result
= ReadLong(signatureAndMacBytes
, signatureAndMacSize
, &xbSize
);
448 require_noerr(result
, exit
);
449 require_action(xbSize
> 4, exit
, result
= errSecDecode
);
450 require_action(xbSize
<= *signatureAndMacSize
, exit
, result
= errSecDecode
);
452 uint8_t signatureMac
[CCSHA256_OUTPUT_SIZE
];
453 cchmac(ccsha256_di(), sizeof(m2
), m2
, xbSize
+ 4, encSigDataBlobStart
, signatureMac
);
455 require(xbSize
+ kSHA256HMAC160Bytes
<= *signatureAndMacSize
, exit
);
456 const uint8_t *macStart
= *signatureAndMacBytes
+ xbSize
;
458 // check the outer hmac
459 require_action(0 == cc_cmp_safe(kSHA256HMAC160Bytes
, macStart
, signatureMac
), exit
, result
= errSecDecode
);
464 // Decrypt and copy the signature block
465 AES_CTR_IV0_Transform(sizeof(c
), c
, xbSize
, *signatureAndMacBytes
, xb
);
467 const uint8_t* signaturePacket
= xb
;
468 size_t signaturePacketSize
= xbSize
;
471 result
= ReadShort(&signaturePacket
, &signaturePacketSize
, &pubKeyType
);
472 require_noerr(result
, exit
);
473 require_action(pubKeyType
== 0xF000, exit
, result
= errSecUnimplemented
);
476 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &pubKeySize
);
477 require_noerr(result
, exit
);
478 require_action(pubKeySize
<= signaturePacketSize
, exit
, result
= errSecDecode
);
479 require(((CFIndex
)pubKeySize
) >= 0, exit
);
481 // Add the signature and keyid to the hash.
482 // PUBKEY of our type is 2 bytes of type, 2 bytes of size and size bytes.
483 // Key ID is 4 bytes.
484 cchmac_update(ccsha256_di(), mBContext
, 2 + 4 + pubKeySize
+ 4, xb
);
486 uint8_t mb
[CCSHA256_OUTPUT_SIZE
];
487 cchmac_final(ccsha256_di(), mBContext
, mb
);
489 // Make reference to the deflated key
490 require_action(SecOTRPIEqualToBytes(session
->_them
, signaturePacket
, (CFIndex
)pubKeySize
), exit
, result
= errSecAuthFailed
);
492 signaturePacket
+= pubKeySize
;
493 signaturePacketSize
-= pubKeySize
;
495 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &session
->_theirKeyID
);
496 require_noerr(result
, exit
);
499 result
= ReadLong(&signaturePacket
, &signaturePacketSize
, &sigSize
);
500 require_noerr(result
, exit
);
501 require_action(sigSize
<= signaturePacketSize
, exit
, result
= errSecDecode
);
503 bool bresult
= SecOTRPIVerifySignature(session
->_them
, mb
, sizeof(mb
), signaturePacket
, sigSize
, NULL
);
504 result
= bresult
? errSecSuccess
: errSecDecode
;
505 require_noerr(result
, exit
);
510 bzero(m1
, sizeof(m1
));
511 bzero(m2
, sizeof(m2
));
517 static OSStatus
SecOTRSProcessRevealSignatureMessage(SecOTRSessionRef session
,
518 CFDataRef incomingPacket
,
519 CFMutableDataRef negotiationResponse
)
521 OSStatus result
= errSecParam
;
523 require_action_quiet(session
->_state
== kAwaitingRevealSignature
, exit
, result
= errSecSuccess
);
525 const uint8_t *messageBytes
= CFDataGetBytePtr(incomingPacket
);
526 size_t messageSize
= (size_t)CFDataGetLength(incomingPacket
);
528 result
= SecOTRSExtractRAndTheirDHKey(session
, &messageBytes
, &messageSize
);
529 require_noerr(result
, exit
);
531 result
= SecVerifySignatureAndMac(session
, false, &messageBytes
, &messageSize
);
532 require_noerr(result
, exit
);
534 SecOTRAppendSignatureMessage(session
, negotiationResponse
);
536 session
->_state
= kDone
;
537 result
= errSecSuccess
;
539 CFDataPerformWithHexString(negotiationResponse
, ^(CFStringRef responseString
) {
540 secnotice("otr", "%@ Sending Signature message: %@", session
, responseString
);
545 if (result
!= errSecSuccess
) {
546 CFDataPerformWithHexString(incomingPacket
, ^(CFStringRef incomingString
) {
547 secnotice("otr", "%@ Failed to process reveal sig message (%d): %@", session
, (int)result
, incomingString
);
553 static OSStatus
SecOTRSProcessSignatureMessage(SecOTRSessionRef session
,
554 CFDataRef incomingPacket
,
555 CFMutableDataRef negotiationResponse
)
557 OSStatus result
= errSecParam
;
559 require_action_quiet(session
->_state
== kAwaitingSignature
, exit
, result
= errSecSuccess
);
561 const uint8_t *messageBytes
= CFDataGetBytePtr(incomingPacket
);
562 size_t messageSize
= (size_t)CFDataGetLength(incomingPacket
);
564 OTRMessageType messageType
;
565 result
= ReadHeader(&messageBytes
, &messageSize
, &messageType
);
566 require_noerr(result
, exit
);
567 require_action(messageType
== kSignatureMessage
, exit
, result
= errSecDecode
);
569 result
= SecVerifySignatureAndMac(session
, true, &messageBytes
, &messageSize
);
570 require_noerr(result
, exit
);
572 CFReleaseNull(session
->_receivedDHKeyMessage
);
573 session
->_state
= kDone
;
575 result
= errSecSuccess
;
580 OSStatus
SecOTRSProcessPacket(SecOTRSessionRef session
,
581 CFDataRef incomingPacket
,
582 CFMutableDataRef negotiationResponse
)
584 __block OSStatus result
= errSecParam
;
586 require(CFDataGetLength(incomingPacket
) > 0, fail
);
587 dispatch_sync(session
->_queue
, ^{
588 os_activity_initiate("OTR Process Packet", OS_ACTIVITY_FLAG_DEFAULT
, ^{
589 CFDataRef decodedBytes
= SecOTRCopyIncomingBytes(incomingPacket
);
591 const uint8_t* bytes
= CFDataGetBytePtr(decodedBytes
);
592 size_t size
= CFDataGetLength(decodedBytes
);
594 OTRMessageType packetType
= kInvalidMessage
;
595 if (ReadHeader(&bytes
, &size
, &packetType
))
596 packetType
= kInvalidMessage
;
598 CFMutableDataRef destinationMessage
;
599 if (session
->_textOutput
) {
600 destinationMessage
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
602 destinationMessage
= CFRetainSafe(negotiationResponse
);
605 switch (packetType
) {
607 result
= SecOTRSProcessDHMessage(session
, decodedBytes
, destinationMessage
);
610 result
= SecOTRSProcessDHKeyMessage(session
, decodedBytes
, destinationMessage
);
612 case kRevealSignatureMessage
:
613 result
= SecOTRSProcessRevealSignatureMessage(session
, decodedBytes
, destinationMessage
);
615 case kSignatureMessage
:
616 result
= SecOTRSProcessSignatureMessage(session
, decodedBytes
, destinationMessage
);
619 result
= errSecDecode
;
623 if (result
!= errSecSuccess
) {
624 CFDataPerformWithHexString(decodedBytes
, ^(CFStringRef bytesString
) {
625 secnotice("session", "%@ Error %d processing packet type %d, session state %d, keyid %d, myKey %p, myNextKey %p, theirKeyId %d, theirKey %p, theirPreviousKey %p, bytes %@", session
, (int)result
, packetType
, session
->_state
, session
->_keyID
, session
->_myKey
, session
->_myNextKey
, session
->_theirKeyID
, session
->_theirKey
, session
->_theirPreviousKey
, bytesString
);
630 if (session
->_textOutput
) {
631 SecOTRPrepareOutgoingBytes(destinationMessage
, negotiationResponse
);
633 CFReleaseSafe(destinationMessage
);
634 CFReleaseSafe(decodedBytes
);