Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
472 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
using System; | ||
using System.Linq; | ||
using Libplanet.Crypto; | ||
using Libplanet.KeyStore; | ||
using Xunit; | ||
|
||
namespace Libplanet.Tests.KeyStore | ||
{ | ||
public abstract class KeyStoreTest<T> | ||
where T : IKeyStore | ||
{ | ||
public abstract T KeyStore { get; } | ||
|
||
[Fact] | ||
public void List() | ||
{ | ||
Assert.Empty(KeyStore.List()); | ||
Assert.Empty(KeyStore.ListIds()); | ||
|
||
var key = new PrivateKey(); | ||
ProtectedPrivateKey ppk = ProtectedPrivateKey.Protect(key, "pass"); | ||
Guid id = KeyStore.Add(ppk); | ||
|
||
Assert.Single(KeyStore.List()); | ||
Tuple<Guid, ProtectedPrivateKey> pair = KeyStore.List().First(); | ||
Assert.Equal(id, pair.Item1); | ||
Assert.Equal(ppk.Address, pair.Item2.Address); | ||
Assert.Equal(new[] { id }, KeyStore.ListIds()); | ||
|
||
var key2 = new PrivateKey(); | ||
ProtectedPrivateKey ppk2 = ProtectedPrivateKey.Protect(key2, "pass"); | ||
Guid id2 = KeyStore.Add(ppk2); | ||
|
||
Assert.Equal( | ||
new[] { (id, ppk.Address), (id2, ppk2.Address) }.ToHashSet(), | ||
KeyStore.List().Select(tuple => (tuple.Item1, tuple.Item2.Address)).ToHashSet() | ||
); | ||
Assert.Equal( | ||
new[] { id, id2 }.ToHashSet(), | ||
KeyStore.ListIds().ToHashSet() | ||
); | ||
} | ||
|
||
[Fact] | ||
public void Get() | ||
{ | ||
var key = new PrivateKey(); | ||
ProtectedPrivateKey ppk = ProtectedPrivateKey.Protect(key, "pass"); | ||
Guid id = KeyStore.Add(ppk); | ||
|
||
Assert.Equal(ppk.Address, KeyStore.Get(id).Address); | ||
Assert.Equal(key, KeyStore.Get(id).Unprotect("pass")); | ||
Assert.Throws<NoKeyException>(() => KeyStore.Get(Guid.NewGuid())); | ||
} | ||
|
||
[Fact] | ||
public void Add() | ||
{ | ||
var key = new PrivateKey(); | ||
ProtectedPrivateKey ppk = ProtectedPrivateKey.Protect(key, "pass"); | ||
Guid id = KeyStore.Add(ppk); | ||
var key2 = new PrivateKey(); | ||
ProtectedPrivateKey ppk2 = ProtectedPrivateKey.Protect(key2, "pass"); | ||
Guid id2 = KeyStore.Add(ppk2); | ||
|
||
// Key ids should be unique. | ||
Assert.NotEqual(id, id2); | ||
|
||
Assert.Equal(ppk.Address, KeyStore.Get(id).Address); | ||
Assert.Equal(ppk2.Address, KeyStore.Get(id2).Address); | ||
|
||
(Guid, Address Address) ToSimplePair(Tuple<Guid, ProtectedPrivateKey> pair) => | ||
(pair.Item1, pair.Item2.Address); | ||
|
||
Assert.Contains((id, ppk.Address), KeyStore.List().Select(ToSimplePair)); | ||
Assert.Contains((id2, ppk2.Address), KeyStore.List().Select(ToSimplePair)); | ||
} | ||
|
||
[Fact] | ||
public void Remove() | ||
{ | ||
var key = new PrivateKey(); | ||
Guid id = KeyStore.Add(ProtectedPrivateKey.Protect(key, "pass")); | ||
|
||
Assert.Throws<NoKeyException>(() => KeyStore.Remove(Guid.NewGuid())); | ||
Assert.Equal(new[] { id }, KeyStore.ListIds()); | ||
|
||
KeyStore.Remove(id); | ||
Assert.Throws<NoKeyException>(() => KeyStore.Get(id)); | ||
Assert.Empty(KeyStore.ListIds()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
using System; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
using Libplanet.Crypto; | ||
using Libplanet.KeyStore; | ||
using Xunit; | ||
|
||
namespace Libplanet.Tests.KeyStore | ||
{ | ||
public class Web3KeyStoreTest : KeyStoreTest<Web3KeyStore>, IDisposable | ||
{ | ||
public Web3KeyStoreTest() | ||
{ | ||
var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
KeyStore = new Web3KeyStore(tempDir); | ||
} | ||
|
||
public override Web3KeyStore KeyStore { get; } | ||
|
||
[Fact] | ||
public void Constructor() | ||
{ | ||
// Constructor creates a directory if needed. | ||
Assert.True(Directory.Exists(KeyStore.Path)); | ||
} | ||
|
||
[Fact] | ||
public void DefaultKeyStore() | ||
{ | ||
string path = Web3KeyStore.DefaultKeyStore.Path; | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) || | ||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || | ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | ||
{ | ||
Assert.Equal( | ||
Path.Combine( | ||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), | ||
".config", | ||
"planetarium", | ||
"keystore" | ||
), | ||
path | ||
); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
Assert.Equal( | ||
Path.Combine( | ||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), | ||
"planetarium", | ||
"keystore" | ||
), | ||
path | ||
); | ||
} | ||
} | ||
|
||
[Fact] | ||
public void Load() | ||
{ | ||
string idStr = "c8b0c0b1-82a0-41cd-9528-a22a0f208dee"; | ||
Guid id = Guid.Parse(idStr); | ||
var path = Path.Combine(KeyStore.Path, $"UTC--2020-03-23T00-00-00Z--{idStr}"); | ||
var key = new PrivateKey(); | ||
ProtectedPrivateKey ppk = ProtectedPrivateKey.Protect(key, "pass"); | ||
using (var f = new FileStream(path, FileMode.Create)) | ||
{ | ||
ppk.WriteJson(f, id); | ||
} | ||
|
||
Assert.Equal(new[] { id }, KeyStore.ListIds()); | ||
Assert.Equal(ppk.Address, KeyStore.Get(id).Address); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
Directory.Delete(KeyStore.Path, true); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#nullable enable | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Libplanet.KeyStore | ||
{ | ||
/// <summary> | ||
/// The interface to store <see cref="ProtectedPrivateKey"/>s. An appropriate implementation | ||
/// should be used according to a running platform. | ||
/// </summary> | ||
public interface IKeyStore | ||
{ | ||
/// <summary> | ||
/// Lists all keys IDs in the key store. | ||
/// </summary> | ||
/// <returns>All keys IDs in the key store. The order is undefined and not deterministic. | ||
/// </returns> | ||
public IEnumerable<Guid> ListIds(); | ||
|
||
/// <summary> | ||
/// Lists all keys in the key store. | ||
/// </summary> | ||
/// <returns>All keys in the key store. The order is undefined and not deterministic. | ||
/// </returns> | ||
public IEnumerable<Tuple<Guid, ProtectedPrivateKey>> List(); | ||
|
||
/// <summary> | ||
/// Looks for a key having the requested <paramref name="id"/> in the key store. | ||
/// </summary> | ||
/// <param name="id">The key ID to look for.</param> | ||
/// <returns>The found key.</returns> | ||
/// <exception cref="NoKeyException">Thrown when there are no key of the given | ||
/// <paramref name="id"/>.</exception> | ||
public ProtectedPrivateKey Get(Guid id); | ||
|
||
/// <summary> | ||
/// Adds a new <paramref name="key"/> into the key store. | ||
/// </summary> | ||
/// <param name="key">A key to add.</param> | ||
/// <returns>The id of the added <paramref name="key"/>.</returns> | ||
/// <exception cref="ArgumentNullException">Thrown when <c>null</c> is passed to | ||
/// <paramref name="key"/>.</exception> | ||
public Guid Add(ProtectedPrivateKey key); | ||
|
||
/// <summary> | ||
/// Removes a key having the given <pramref name="id"/> from the key store. | ||
/// </summary> | ||
/// <param name="id">The key ID to remove.</param> | ||
/// <exception cref="NoKeyException">Thrown when there is no key having | ||
/// the given <paramref name="id"/>.</exception> | ||
public void Remove(Guid id); | ||
} | ||
} | ||
#nullable restore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
|
||
namespace Libplanet.KeyStore | ||
{ | ||
/// <summary> | ||
/// Serves as the base class for exceptions thrown by <see cref="IKeyStore"/> | ||
/// implementations. | ||
/// </summary> | ||
public abstract class KeyStoreException : Exception | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance with the given <paramref name="message"/>. | ||
/// </summary> | ||
/// <param name="message">A descriptive error message for programmers. | ||
/// Goes to <see cref="System.Exception.Message"/>.</param> | ||
public KeyStoreException(string message) | ||
: base(message) | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
using System; | ||
|
||
namespace Libplanet.KeyStore | ||
{ | ||
/// <summary> | ||
/// The exception that is thrown when there is no <see cref="ProtectedPrivateKey"/> | ||
/// with a given key ID in an <see cref="IKeyStore"/>. | ||
/// </summary> | ||
public class NoKeyException : KeyStoreException | ||
{ | ||
/// <summary> | ||
/// Instantiates a new exception object with proper metadata. | ||
/// </summary> | ||
/// <param name="keyId">The key ID tried to look for. | ||
/// It is automatically included to the <see cref="System.Exception.Message"/> | ||
/// string. | ||
/// </param> | ||
/// <param name="message">A descriptive error message for programmers. | ||
/// Goes to <see cref="System.Exception.Message"/>.</param> | ||
public NoKeyException(Guid keyId, string message) | ||
: base($"{message}: {keyId}") | ||
{ | ||
KeyId = keyId; | ||
} | ||
|
||
/// <summary> | ||
/// The key ID tried to look for. | ||
/// </summary> | ||
public Guid KeyId { get; } | ||
} | ||
} |
Oops, something went wrong.