Skip to content

Commit

Permalink
Refactor: Improve ParseKey (#27)
Browse files Browse the repository at this point in the history
* fix: parsing of inputUserKey
test:fix: exception in parsing key tests

* refactor: ParseKey more gracefully

* remove: keys from exception message
  • Loading branch information
Romazes authored Dec 4, 2024
1 parent cf6731c commit 9d63170
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 14 deletions.
6 changes: 3 additions & 3 deletions QuantConnect.CoinbaseBrokerage.Tests/CoinbaseApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ public void Setup()
CoinbaseApi = CreateCoinbaseApi(name, priavteKey);
}

[TestCase("", "", typeof(ArgumentOutOfRangeException))]
[TestCase("organizations/2c7dhs-a3a3-4acf-aa0c-f68584f34c37/apiKeys/41090ffa-asd2-4040-815f-afaf63747e35", "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIPcJGfXYEdLQi0iFj1xvGfPwuRNoeddwuKS4xL2NrlGWpoAoGCCqGSM49\nAwEHoUQDQgAEclN+asd/EhJ3UjOWkHmP/iqGBv5NkNJ75bUq\nVgxS4aU3/djHiIuSf27QasdOFIDGJLmOn7YiQ==\n-----END EC PRIVATE KEY-----\n", typeof(CryptographicException))]
[TestCase("organizations/2c7dhs-a3a3-4acf-aa0c-f68584f34c37/apiKeys/41090ffa-asd2-4040-815f-afaf63747e35", "MHcCAQEEIPcJGfXYEdLQi0iFj1xvGfPwuRNoeddwuKS4xL2NrlGWpoAoGCCqGSM49\nAwEHoUQDQgAEclN+asd/EhJ3UjOWkHmP/iqGBv5NkNJ75bUq\nVgxS4aU3/djHiIuSf27QasdOFIDGJLmOn7YiQ==", typeof(CryptographicException))]
[TestCase("", "", typeof(InvalidOperationException))]
[TestCase("organizations/2c7dhs-a3a3-4acf-aa0c-f68584f34c37/apiKeys/41090ffa-asd2-4040-815f-afaf63747e35", "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIPcJGfXYEdLQi0iFj1xvGfPwuRNoeddwuKS4xL2NrlGWpoAoGCCqGSM49\nAwEHoUQDQgAEclN+asd/EhJ3UjOWkHmP/iqGBv5NkNJ75bUq\nVgxS4aU3/djHiIuSf27QasdOFIDGJLmOn7YiQ==\n-----END EC PRIVATE KEY-----\n", typeof(InvalidOperationException))]
[TestCase("organizations/2c7dhs-a3a3-4acf-aa0c-f68584f34c37/apiKeys/41090ffa-asd2-4040-815f-afaf63747e35", "MHcCAQEEIPcJGfXYEdLQi0iFj1xvGfPwuRNoeddwuKS4xL2NrlGWpoAoGCCqGSM49\nAwEHoUQDQgAEclN+asd/EhJ3UjOWkHmP/iqGBv5NkNJ75bUq\nVgxS4aU3/djHiIuSf27QasdOFIDGJLmOn7YiQ==", typeof(InvalidOperationException))]
public void InvalidAuthenticationCredentialsShouldThrowException(string name, string privateKey, Type expectedException)
{
try
Expand Down
46 changes: 35 additions & 11 deletions QuantConnect.CoinbaseBrokerage/Api/CoinbaseApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,36 @@ public CoinbaseApiClient(string name, string privateKey, string restApiUrl, int
_restClient = new RestClient(restApiUrl);
_rateGate = new RateGate(maxRequestsPerSecond, Time.OneSecond);

_privateKey = ECDsa.Create();
_privateKey.ImportECPrivateKey(Convert.FromBase64String(ParseKey(privateKey)), out _);
_privateKey = CreateECDsaByPrivateKey(privateKey);
}

/// <summary>
/// Creates an ECDsa instance from a PEM-formatted EC private key.
/// </summary>
/// <param name="userInputPrivateKey">
/// A string containing the PEM-formatted EC private key, including markers like
/// "-----BEGIN EC PRIVATE KEY-----" and "-----END EC PRIVATE KEY-----".
/// </param>
/// <returns>
/// An <see cref="ECDsa"/> instance initialized with the provided private key.
/// </returns>
private ECDsa CreateECDsaByPrivateKey(string userInputPrivateKey)
{
var parsedKey = default(string);
try
{
var privateKey = ECDsa.Create();
parsedKey = ParseKey(userInputPrivateKey);
privateKey.ImportECPrivateKey(Convert.FromBase64String(parsedKey), out _);
return privateKey;
}
catch (Exception ex)
{
throw new InvalidOperationException($"{nameof(CoinbaseApiClient)}.{nameof(CreateECDsaByPrivateKey)}: Failed to create ECDsa from the provided private key.\n" +
"Please refer to the documentation for the correct key format: " +
$"https://www.quantconnect.com/docs/v2/cloud-platform/live-trading/brokerages/coinbase#02-Account-Types\n\n Error: {ex.Message}",
ex);
}
}

/// <summary>
Expand Down Expand Up @@ -196,16 +224,12 @@ private string GenerateToken(string uri = null)
/// </remarks>
internal string ParseKey(string key)
{
var keyLines = key.Split('\n', StringSplitOptions.RemoveEmptyEntries).ToList();

// Check if the first and last lines are the BEGIN/END markers, and remove them if present
if (keyLines.First().Contains("BEGIN") && keyLines.Last().Contains("END"))
{
keyLines.RemoveAt(0); // Remove the first line
keyLines.RemoveAt(keyLines.Count - 1); // Remove the last line
}
var keyLines = key
.Replace("-----BEGIN EC PRIVATE KEY-----", string.Empty)
.Replace("-----END EC PRIVATE KEY-----", string.Empty)
.Split('\n', StringSplitOptions.RemoveEmptyEntries);

return string.Join("", keyLines);
return string.Join(string.Empty, keyLines);
}

/// <summary>
Expand Down

0 comments on commit 9d63170

Please sign in to comment.