protected override async Task<ArraySegment<byte>> QueryAsync(ArraySegment<byte> message, CancellationToken cancellationToken = default(CancellationToken)) { var fidoMessage = new FidoU2FHidMessage(GetChannel(), U2FHidCommand.Message, message); var response = await QueryLowLevelAsync(fidoMessage, cancellationToken); return response.Data; }
public async Task<U2FHidDeviceInfo> InitAsync(ArraySegment<byte> nonce, CancellationToken cancellationToken = default(CancellationToken)) { if (nonce.Count != InitNonceSize) { throw new ArgumentException( $"Nonce should be exactly {InitNonceSize} bytes but is {nonce.Count} bytes long instead.", nameof(nonce)); } log.Info("Sending initialization"); var message = new FidoU2FHidMessage(BroadcastChannel, U2FHidCommand.Init, nonce); var answer = await QueryLowLevelAsync(message, cancellationToken); return OnInitAnswered(answer, nonce); }
U2FHidDeviceInfo OnInitAnswered(FidoU2FHidMessage response, ArraySegment<byte> requestNonce) { log.Info("Initialization response received"); ArraySegment<byte> responseNonce; var deviceInfo = MessageCodec.DecodeInitResponse(response.Data, out responseNonce); if (!responseNonce.ContentEquals(requestNonce)) { throw new Exception("Invalid nonce, not an answer to our init request"); } DeviceInfo = deviceInfo; return deviceInfo; }
public static async Task WriteFidoU2FHidMessageAsync([NotNull] this IHidDevice device, FidoU2FHidMessage message, CancellationToken cancellationToken) { if (device == null) throw new ArgumentNullException(nameof(device)); log.Debug($"Sending U2FHid message {message.Command} on channel 0x{message.Channel:X8} with {message.Data.Count} bytes of data"); var report = device.CreateOutputReport(); var packets = MakeOutputPackets(report.Data.Count, message); packets.Item1.WriteTo(report.Data); await device.SendOutputReportAsync(report, cancellationToken); foreach (var continuation in packets.Item2) { await device.SendOutputReportAsync(ToOutputReport(device, continuation), cancellationToken); } }
public static async Task WriteFidoU2FHidMessageAsync([NotNull] this IHidDevice device, FidoU2FHidMessage message, CancellationToken cancellationToken) { if (device == null) { throw new ArgumentNullException(nameof(device)); } log.Debug($"Sending U2FHid message {message.Command} on channel 0x{message.Channel:X8} with {message.Data.Count} bytes of data"); var report = device.CreateOutputReport(); var packets = MakeOutputPackets(report.Data.Count, message); packets.Item1.WriteTo(report.Data); await device.SendOutputReportAsync(report, cancellationToken); foreach (var continuation in packets.Item2) { await device.SendOutputReportAsync(ToOutputReport(device, continuation), cancellationToken); } }
static Tuple <InitializationPacket, List <ContinuationPacket> > MakeOutputPackets( int paketLength, FidoU2FHidMessage message) { var availableInInit = paketLength - InitializationPacket.NoDataSize; var availableInContinuation = paketLength - ContinuationPacket.NoDataSize; var data = message.Data; var init = new InitializationPacket { ChannelIdentifier = message.Channel, CommandIdentifier = (byte)message.Command, PayloadLength = (ushort)data.Count, Data = new ArraySegment <byte>(data.Array, data.Offset, Math.Min(data.Count, availableInInit)) }; var sizeHandled = init.Data.Count; var continuations = new List <ContinuationPacket>(); byte sequence = 0; while (sizeHandled < data.Count) { var continuation = new ContinuationPacket { ChannelIdentifier = message.Channel, PaketSequence = sequence, Data = new ArraySegment <byte>(data.Array, data.Offset + sizeHandled, Math.Min(data.Count - sizeHandled, availableInContinuation)) }; continuations.Add(continuation); sizeHandled += continuation.Data.Count; sequence += 1; } return(Tuple.Create(init, continuations)); }
static Tuple<InitializationPacket, List<ContinuationPacket>> MakeOutputPackets( int paketLength, FidoU2FHidMessage message) { var availableInInit = paketLength - InitializationPacket.NoDataSize; var availableInContinuation = paketLength - ContinuationPacket.NoDataSize; var data = message.Data; var init = new InitializationPacket { ChannelIdentifier = message.Channel, CommandIdentifier = (byte)message.Command, PayloadLength = (ushort)data.Count, Data = new ArraySegment<byte>(data.Array, data.Offset, Math.Min(data.Count, availableInInit)) }; var sizeHandled = init.Data.Count; var continuations = new List<ContinuationPacket>(); byte sequence = 0; while (sizeHandled < data.Count) { var continuation = new ContinuationPacket { ChannelIdentifier = message.Channel, PaketSequence = sequence, Data = new ArraySegment<byte>(data.Array, data.Offset + sizeHandled, Math.Min(data.Count - sizeHandled, availableInContinuation)) }; continuations.Add(continuation); sizeHandled += continuation.Data.Count; sequence += 1; } return Tuple.Create(init, continuations); }
/// <summary> /// Sends a transaction to the device, which immediately echoes the same data back. /// This command is defined to be an uniform function for debugging, latency and performance measurements. /// </summary> public async Task<ArraySegment<byte>> Ping(ArraySegment<byte> pingData, CancellationToken cancellationToken = default(CancellationToken)) { var message = new FidoU2FHidMessage(GetChannel(), U2FHidCommand.Ping, pingData); var response = await QueryLowLevelAsync(message, cancellationToken); if (!pingData.ContentEquals(response.Data)) { throw new InvalidPingResponseException("The device didn't echo back our ping message."); } return response.Data; }
private static U2FHidErrors GetError(FidoU2FHidMessage message) { Debug.Assert(message.Command == U2FHidCommand.Error); if (message.Data.Count < 1) { throw new Exception("Bad length for error"); } var error = (U2FHidErrors) message.Data.AsEnumerable().First(); return error; }
static void ThrowForError(FidoU2FHidMessage message) { var error = GetError(message); throw new Exception("Error: " + EnumDescription.Get(error)); }
async Task<FidoU2FHidMessage> QueryLowLevelAsync(FidoU2FHidMessage query, CancellationToken cancellationToken, bool throwErrors = true) { try { await device.WriteFidoU2FHidMessageAsync(query, cancellationToken); var init = await device.ReadFidoU2FHidMessageAsync(cancellationToken); if (init.Channel != query.Channel) { throw new Exception( $"Bad channel in query answer (0x{init.Channel:X8} but expected 0x{query.Channel:X8})"); } if (init.Command == U2FHidCommand.Error) { if (throwErrors) { ThrowForError(init); } return init; } if (init.Command != query.Command) { throw new Exception($"Bad command in query answer ({init.Command} but expected {query.Command})"); } return init; } catch (DeviceNotConnectedException exception) { throw new KeyGoneException("The key isn't connected anymore", exception); } }
/// <summary> /// The lock command places an exclusive lock for one channel to communicate with the device. /// As long as the lock is active, any other channel trying to send a message will fail. /// In order to prevent a stalling or crashing application to lock the device indefinitely, a lock time up to /// 10 seconds may be set. An application requiring a longer lock has to send repeating lock commands to /// maintain the lock. /// </summary> public async Task<bool> LockAsync(byte timeInSeconds, CancellationToken cancellationToken = default (CancellationToken)) { if (timeInSeconds > 10) { throw new ArgumentOutOfRangeException(nameof(timeInSeconds), timeInSeconds, "Lock time must be between 0 and 10s"); } var data = new [] { timeInSeconds }.Segment(); var message = new FidoU2FHidMessage(GetChannel(), U2FHidCommand.Lock, data); var response = await QueryLowLevelAsync(message, cancellationToken, false); if (response.Command == U2FHidCommand.Error) { var errorCode = GetError(response); if (errorCode == U2FHidErrors.Busy) { return false; } ThrowForError(response); } return true; }
public Task WinkAsync(CancellationToken cancellationToken = default(CancellationToken)) { var message = new FidoU2FHidMessage(GetChannel(), U2FHidCommand.Wink); return QueryLowLevelAsync(message, cancellationToken); }
static unsafe void Wink(Win32HidDevice device, byte b1, byte b2, byte b3, byte b4) { var msg = new FidoU2FHidMessage( (uint)(unchecked (b1 << 24 | b2 << 16 | b3 << 8 | b4)), U2FHidCommand.Wink); device.WriteFidoU2FHidMessageAsync(msg, CancellationToken.None).Wait(); var caps = device.Information.Capabilities; var bufferOut = new byte[caps.OutputReportByteLength]; fixed (byte* pBuffer = bufferOut) { var intPtr = new IntPtr(pBuffer); var task = Kernel32Dll.ReadFileAsync(device.Handle, intPtr, bufferOut.Length); var read = task.Result; Console.WriteLine("Read {0} bytes", read); } WriteBuffer(bufferOut); }