Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions MegaApiClient/MegaApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,7 @@ private TResponse RequestCore<TResponse>(RequestBase request, byte[] key)
where TResponse : class
{
var dataRequest = JsonConvert.SerializeObject(new object[] { request });
var uri = GenerateUrl(request.QueryArguments);
var uri = GenerateUrl(request.QueryArguments, request.UseSession);
object jsonData = null;
var attempt = 0;
var apiCode = ApiResultCode.Ok;
Expand Down Expand Up @@ -1182,15 +1182,15 @@ private void Wait(TimeSpan retryDelay)
#endif
}

private Uri GenerateUrl(Dictionary<string, string> queryArguments)
private Uri GenerateUrl(Dictionary<string, string> queryArguments, bool useSession = true)
{
var query = new Dictionary<string, string>(queryArguments)
{
["id"] = (_sequenceIndex++ % uint.MaxValue).ToString(CultureInfo.InvariantCulture),
["ak"] = _options.ApplicationKey
};

if (!string.IsNullOrEmpty(_sessionId))
if (useSession && !string.IsNullOrEmpty(_sessionId))
{
query["sid"] = _sessionId;
}
Expand Down
96 changes: 57 additions & 39 deletions MegaApiClient/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,54 +136,72 @@ public void OnDeserialized(StreamingContext ctx)
return;
}

// There are cases where the SerializedKey property contains multiple keys separated with /
// This can occur when a folder is shared and the parent is shared too.
// Both keys are working so we use the first one
var serializedKey = SerializedKey.Split('/')[0];
var splitPosition = serializedKey.IndexOf(":", StringComparison.Ordinal);
var encryptedKey = serializedKey.Substring(splitPosition + 1).FromBase64();

// If node is shared, we need to retrieve shared masterkey
if (_sharedKeys != null)
// The SerializedKey property can contain multiple keys separated with /
// This occurs when a folder is shared and the parent is shared too, or for
// shared folder links where the owner's key and share key are both present.
// Try each key and use the first one that produces valid attributes.
var serializedKeys = SerializedKey.Split('/');

foreach (var serializedKey in serializedKeys)
{
var splitPosition = serializedKey.IndexOf(":", StringComparison.Ordinal);
if (splitPosition < 0)
{
continue;
}

var handle = serializedKey.Substring(0, splitPosition);
var sharedKey = _sharedKeys.FirstOrDefault(x => x.Id == handle);
if (sharedKey != null)
var encryptedKey = serializedKey.Substring(splitPosition + 1).FromBase64();

// If node is shared, we need to retrieve shared masterkey
var usedMasterKey = _masterKey;
byte[] sharedKeyValue = null;
if (_sharedKeys != null)
{
_masterKey = Crypto.DecryptKey(sharedKey.Key.FromBase64(), _masterKey);
if (Type == NodeType.Directory)
{
SharedKey = _masterKey;
}
else
var sharedKey = _sharedKeys.FirstOrDefault(x => x.Id == handle);
if (sharedKey != null)
{
SharedKey = Crypto.DecryptKey(encryptedKey, _masterKey);
usedMasterKey = Crypto.DecryptKey(sharedKey.Key.FromBase64(), _masterKey);
sharedKeyValue = Type == NodeType.Directory
? usedMasterKey
: Crypto.DecryptKey(encryptedKey, usedMasterKey);
}
}
}

if (encryptedKey.Length != 16 && encryptedKey.Length != 32)
{
// Invalid key size
return;
}
if (encryptedKey.Length != 16 && encryptedKey.Length != 32)
{
continue;
}

FullKey = Crypto.DecryptKey(encryptedKey, _masterKey);
var fullKey = Crypto.DecryptKey(encryptedKey, usedMasterKey);
byte[] nodeKey;
byte[] iv = null;
byte[] metaMac = null;

if (Type == NodeType.File)
{
Crypto.GetPartsFromDecryptedKey(FullKey, out var iv, out var metaMac, out var fileKey);
if (Type == NodeType.File)
{
Crypto.GetPartsFromDecryptedKey(fullKey, out iv, out metaMac, out nodeKey);
}
else
{
nodeKey = fullKey;
}

var attrs = Crypto.DecryptAttributes(SerializedAttributes.FromBase64(), nodeKey);

FullKey = fullKey;
Key = nodeKey;
Iv = iv;
MetaMac = metaMac;
Key = fileKey;
}
else
{
Key = FullKey;
SharedKey = sharedKeyValue;
Attributes = attrs;

if (attrs?.Name != null && !attrs.Name.StartsWith("Attribute deserialization failed"))
{
break;
}
}

Attributes = Crypto.DecryptAttributes(SerializedAttributes.FromBase64(), Key);
FileAttributes = DeserializeFileAttributes(SerializedFileAttributes);
}
}
Expand Down Expand Up @@ -278,12 +296,12 @@ private bool IsShareRoot
{
return true;
}
else

return _node.SerializedKey.Split('/').Any(key =>
{
var serializedKey = _node.SerializedKey.Split('/')[0];
var splitPosition = serializedKey.IndexOf(":", StringComparison.Ordinal);
return serializedKey.Substring(0, splitPosition) == Id;
}
var splitPosition = key.IndexOf(":", StringComparison.Ordinal);
return splitPosition >= 0 && key.Substring(0, splitPosition) == Id;
});
}
}
}
Expand Down
1 change: 1 addition & 0 deletions MegaApiClient/Serialization/DownloadUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public DownloadUrlRequest(INode node)
if (node is PublicNode publicNode)
{
QueryArguments["n"] = publicNode.ShareId;
UseSession = false;
}
}

Expand Down
5 changes: 5 additions & 0 deletions MegaApiClient/Serialization/GetNodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public GetNodesRequest(string shareId = null)
: base("f")
{
C = 1;
Ca = 1;
UseSession = false;

if (shareId != null)
{
Expand All @@ -27,6 +29,9 @@ public GetNodesRequest(string shareId = null)

[JsonProperty("r")]
public int R { get; private set; }

[JsonProperty("ca")]
public int Ca { get; private set; }
}

internal class GetNodesResponse
Expand Down
4 changes: 4 additions & 0 deletions MegaApiClient/Serialization/RequestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ protected RequestBase(string action)
{
Action = action;
QueryArguments = new Dictionary<string, string>();
UseSession = true;
}

[JsonProperty("a")]
public string Action { get; private set; }

[JsonIgnore]
public Dictionary<string, string> QueryArguments { get; }

[JsonIgnore]
public bool UseSession { get; protected set; }
}
}