static CFStringRef
decryptString(SecKeyRef wrapKey, CFDataRef iv, CFDataRef wrappedPassword)
{
	CFStringRef retval = NULL;
	CFDataRef retData = NULL;
 	CFErrorRef error = NULL;

	SecTransformRef decryptTrans = SecDecryptTransformCreate(wrapKey, &error);
    if(error == NULL) {
  		SecTransformRef group = SecTransformCreateGroupTransform();
      
		SecTransformRef decodeTrans = SecDecodeTransformCreate(kSecBase64Encoding, &error);
  		if(error == NULL) SecTransformSetAttribute(decodeTrans, kSecTransformInputAttributeName, wrappedPassword, &error);
        
		if(error == NULL) SecTransformSetAttribute(decryptTrans, kSecEncryptionMode, kSecModeCBCKey, &error);
 		if(error == NULL) SecTransformSetAttribute(decryptTrans, kSecPaddingKey, kSecPaddingPKCS7Key, &error);
		if(error == NULL) SecTransformSetAttribute(decryptTrans, kSecIVKey, iv, &error);
 		SecTransformConnectTransforms(decodeTrans, kSecTransformOutputAttributeName, decryptTrans, kSecTransformInputAttributeName, group, &error);
		CFRelease(decodeTrans);  
		CFRelease(decryptTrans);
        if(error == NULL) retData =  SecTransformExecute(group, &error);
        
        if(error == NULL) retval = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, retData, kCFStringEncodingMacRoman);
        else secDebug(ASL_LEVEL_ERR, "Failed to decrypt recovery password\n", NULL);
        CFRelease(group);
	}
   return retval;
}
TagLib::ByteVector TagLib::DecodeBase64(const TagLib::ByteVector& input)
{
    ByteVector result;

    CFErrorRef error;
    SFB::SecTransform decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error);
    if(!decoder) {
        LOGGER_WARNING("org.sbooth.AudioEngine", "SecDecodeTransformCreate failed: " << error);
        return TagLib::ByteVector::null;
    }

    SFB::CFData sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), (CFIndex)input.size(), kCFAllocatorNull);
    if(!sourceData)
        return TagLib::ByteVector::null;

    if(!SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, sourceData, &error)) {
        LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformSetAttribute failed: " << error);
        return TagLib::ByteVector::null;
    }

    SFB::CFData decodedData = (CFDataRef)SecTransformExecute(decoder, &error);
    if(!decodedData)
        return TagLib::ByteVector::null;

    result.setData((const char *)CFDataGetBytePtr((CFDataRef)decodedData), (TagLib::uint)CFDataGetLength((CFDataRef)decodedData));

    return result;
}
static CFDataRef
b64decode(CFDataRef input)
{
	CFDataRef retval = NULL;
    CFErrorRef error = NULL;
	SecTransformRef decodeTrans = SecDecodeTransformCreate(kSecBase64Encoding, &error);
    if(error == NULL) SecTransformSetAttribute(decodeTrans, kSecTransformInputAttributeName, input, &error);
    if(error == NULL) retval = SecTransformExecute(decodeTrans, &error);
	if(decodeTrans) CFRelease(decodeTrans);
    return retval;
}
TagLib::ByteVector TagLib::DecodeBase64(const TagLib::ByteVector& input)
{
#if USE_SECURITY_FRAMEWORK
	ByteVector result;

	CFErrorRef error;
	SecTransformRef decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error);
    if(nullptr == decoder) {
		CFShow(error); 
		return TagLib::ByteVector::null;
	}

	CFDataRef sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), input.size(), kCFAllocatorNull);
	if(nullptr == sourceData) {
		CFRelease(decoder), decoder = nullptr;

		return TagLib::ByteVector::null;
	}

    if(!SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, sourceData, &error)) {
		CFShow(error); 

		CFRelease(sourceData), sourceData = nullptr;
		CFRelease(decoder), decoder = nullptr;

		return TagLib::ByteVector::null;
	}

	CFTypeRef decodedData = SecTransformExecute(decoder, &error);
	if(nullptr == decodedData) {
		CFShow(error); 

		CFRelease(sourceData), sourceData = nullptr;
		CFRelease(decoder), decoder = nullptr;

		return TagLib::ByteVector::null;
	}

	result.setData((const char *)CFDataGetBytePtr((CFDataRef)decodedData), (TagLib::uint)CFDataGetLength((CFDataRef)decodedData));

	CFRelease(decodedData), decodedData = nullptr;
	CFRelease(sourceData), sourceData = nullptr;
	CFRelease(decoder), decoder = nullptr;
	
	return result;
#else
	ByteVector result;

	BIO *b64 = BIO_new(BIO_f_base64());
	BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

	BIO *bio = BIO_new_mem_buf(reinterpret_cast<void *>(const_cast<char *>(input.data())), input.size());
	bio = BIO_push(b64, bio);

	char inbuf [512];
	int inlen;
	while(0 < (inlen = BIO_read(bio, inbuf, 512)))
		result.append(ByteVector(inbuf, inlen));

	BIO_free_all(bio);
	
	return result;
#endif
}