summaryrefslogtreecommitdiff
path: root/wrappers/csharp/Discovery.cs
diff options
context:
space:
mode:
Diffstat (limited to 'wrappers/csharp/Discovery.cs')
-rw-r--r--wrappers/csharp/Discovery.cs86
1 files changed, 54 insertions, 32 deletions
diff --git a/wrappers/csharp/Discovery.cs b/wrappers/csharp/Discovery.cs
index 183d94f..da4a6ea 100644
--- a/wrappers/csharp/Discovery.cs
+++ b/wrappers/csharp/Discovery.cs
@@ -1,8 +1,10 @@
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
+// Make InsecureTestingSetExtraKey visible to tests
[assembly: InternalsVisibleTo("EduVpnCommonTests")]
namespace EduVpnCommon
@@ -11,12 +13,12 @@ namespace EduVpnCommon
{
/// <summary>
/// Verifies the signature on the JSON server_list.json/organization_list.json file.
- /// If the function returns the signature is valid for the given file type.
+ /// If the function returns, the signature is valid for the given file type.
/// </summary>
/// <param name="signatureFileContent">.minisig signature file contents.</param>
/// <param name="signedJson">Signed .json file contents.</param>
/// <param name="expectedFileName">The file type to be verified, one of <c>"server_list.json"</c> or <c>"organization_list.json"</c>.</param>
- /// <param name="minSignTime">Minimum time for signature. Should be set to at least the time in a previously retrieved file.</param>
+ /// <param name="minSignTime">Minimum time for signature. Should be set to at least the time of the previous signature.</param>
/// <exception cref="ArgumentException">If <c>expectedFileName</c> is not one of the allowed values.</exception>
/// <exception cref="VerifyException">If signature verification fails.</exception>
public static void Verify(
@@ -35,11 +37,20 @@ namespace EduVpnCommon
(ulong) minSignTime.ToUnixTimeSeconds());
}
- if (result != VerifyReturnCode.Ok)
+ switch (result)
{
- if (result == VerifyReturnCode.ErrUnknownExpectedFileName)
- throw new ArgumentException("unknown name", nameof(expectedFileName));
- throw new VerifyException((VerifyErrorCode) result);
+ case VerifyReturnCode.Ok:
+ return;
+ case VerifyReturnCode.ErrUnknownExpectedFileName:
+ throw new ArgumentException("unknown expected file name", nameof(expectedFileName));
+ case VerifyReturnCode.ErrInvalidSignature:
+ throw new InvalidSignatureException();
+ case VerifyReturnCode.ErrInvalidSignatureUnknownKey:
+ throw new InvalidSignatureUnknownKeyException();
+ case VerifyReturnCode.ErrTooOld:
+ throw new SignatureTooOldException();
+ default:
+ throw new UnknownVerifyException((sbyte) result);
}
}
@@ -50,13 +61,17 @@ namespace EduVpnCommon
InsecureTestingSetExtraKey(keyHandle.Slice);
}
- const string VerifyLibName = "eduvpn_verify";
+ const string LibName = "eduvpn_common";
- [DllImport(VerifyLibName)]
+ [DllImport(LibName)]
static extern VerifyReturnCode Verify(GoSlice signatureFileContent, GoSlice signedJson, GoSlice expectedFileName, ulong minSignTime);
- [DllImport(VerifyLibName)] static extern void InsecureTestingSetExtraKey(GoSlice keyStr);
+ [DllImport(LibName)] static extern void InsecureTestingSetExtraKey(GoSlice keyStr);
+ /// <summary>
+ /// Safe auto-disposing Go slice handle.
+ /// Non-copying alternative to `Marshal.AllocHGlobal` etc.
+ /// </summary>
class GoSliceHandle : IDisposable
{
GCHandle gcHandle_;
@@ -68,6 +83,7 @@ namespace EduVpnCommon
GoSliceHandle(Array array, int offset, int count)
{
+ Debug.Assert(offset <= array.Length && /*prevent overflow:*/ count <= array.Length && offset <= array.Length - count);
gcHandle_ = GCHandle.Alloc(array, GCHandleType.Pinned);
var elemSize = Marshal.SizeOf(array.GetType().GetElementType()!);
slice_ = new GoSlice(gcHandle_.AddrOfPinnedObject() + offset * elemSize, count * elemSize);
@@ -76,12 +92,14 @@ namespace EduVpnCommon
public static GoSliceHandle FromArray<T>(ArraySegment<T> segment) where T : struct =>
new GoSliceHandle(segment.Array!, segment.Offset, segment.Count);
+ /// <summary>From string as UTF-8.</summary>
public static GoSliceHandle FromString(string str) =>
FromArray(new ArraySegment<byte>(Encoding.UTF8.GetBytes(str)));
public void Dispose() => gcHandle_.Free();
}
+ // C-compatible structure
readonly struct GoSlice
{
readonly IntPtr data_;
@@ -98,38 +116,42 @@ namespace EduVpnCommon
}
}
- public class VerifyException : Exception
+ /// <summary>Verification failed, do not trust the file.</summary>
+ public abstract class VerifyException : Exception
{
- public VerifyErrorCode Code { get; }
-
- internal VerifyException(VerifyErrorCode code) : base(GetMessage(code)) => Code = code;
+ protected VerifyException(string message) : base(message) { }
+ }
- static string GetMessage(VerifyErrorCode code) => code switch
- {
- VerifyErrorCode.ErrInvalidSignature => "invalid signature",
- VerifyErrorCode.ErrInvalidSignatureUnknownKey => "invalid signature (unknown key)",
- VerifyErrorCode.ErrTooOld => "replay of previous signature (rollback)",
- _ => $"unknown verify error ({code})"
- };
+ /// <summary>Signature is invalid (for the expected file type).</summary>
+ public sealed class InvalidSignatureException : VerifyException
+ {
+ public InvalidSignatureException() : base("invalid signature") { }
}
- public enum VerifyErrorCode
+ /// <summary>Signature was created with an unknown key and has not been verified.</summary>
+ public sealed class InvalidSignatureUnknownKeyException : VerifyException
{
- /// <summary>Signature is invalid (for the expected file type).</summary>
- ErrInvalidSignature = VerifyReturnCode.ErrUnknownExpectedFileName + 1,
+ public InvalidSignatureUnknownKeyException() : base("invalid signature (unknown key)") { }
+ }
- /// <summary>Signature was created with an unknown key and has not been verified.</summary>
- ErrInvalidSignatureUnknownKey,
+ /// <summary>Signature timestamp smaller than specified minimum signing time (rollback).</summary>
+ public sealed class SignatureTooOldException : VerifyException
+ {
+ public SignatureTooOldException() : base("replay of previous signature (rollback)") { }
+ }
- /// <summary>Signature has a timestamp lower than the specified minimum signing time.</summary>
- ErrTooOld
+ /// <summary>Other unknown error.</summary>
+ public sealed class UnknownVerifyException : VerifyException
+ {
+ public UnknownVerifyException(sbyte code) : base($"unknown verify error ({code})") => Debug.Assert(code != 0);
}
- enum VerifyReturnCode
+ enum VerifyReturnCode : sbyte
{
Ok,
- ErrUnknownExpectedFileName
-
- //...
+ ErrUnknownExpectedFileName,
+ ErrInvalidSignature,
+ ErrInvalidSignatureUnknownKey,
+ ErrTooOld
}
-} \ No newline at end of file
+}