//returns: doRetry? private bool CheckHeader(byte[] response, out FidoHidMsgType msgType) { msgType = FidoHidMsgType.Error; if (response.Length < 5) { return(false); } //ignore messages for other channels if (!response.Take(4).SequenceEqual(_channelId)) { return(false); } msgType = (FidoHidMsgType)response[4]; if (msgType != FidoHidMsgType.Error) { return(true); } if (response.Length < 8) { throw new FidoException(FidoError.ProtocolViolation, "error message too short"); } var errorCode = (FidoHidError)response[7]; switch (errorCode) { case FidoHidError.MessageTimeout: throw new FidoException(FidoError.Timeout); case FidoHidError.ChannelBusy: throw new FidoException(FidoError.TokenBusy); default: throw new FidoException(FidoError.ProtocolViolation, $"U2FHID Error: [{errorCode}]"); } }
private async Task SendRequestAsync(FidoHidMsgType msgType, byte[] data = null) { if (data == null) { data = new byte[0]; } var size = data.Length; var payloadData = data.Take(HidReportSize - 7).ToArray(); var payloadBuilder = new ByteArrayBuilder(); payloadBuilder.Append(_channelId); payloadBuilder.Append((byte)msgType); payloadBuilder.Append((byte)(size >> 8 & 0xff)); payloadBuilder.Append((byte)(size & 0xff)); payloadBuilder.Append(payloadData); payloadBuilder.AppendZerosTill(HidReportSize); var report = _hidDevice.CreateReport(); report.Data = payloadBuilder.GetBytes(); if (!await _hidDevice.WriteReportAsync(report, HidTimeoutMs).ConfigureAwait(false)) { throw new FidoException(FidoError.InterruptedIO, "Error writing to token"); } var remainingData = data.Skip(HidReportSize - 7).ToArray(); var seq = 0; while (remainingData.Length > 0) { payloadData = remainingData.Take(HidReportSize - 5).ToArray(); payloadBuilder.Clear(); payloadBuilder.Append(_channelId); payloadBuilder.Append((byte)(0x7f & seq)); payloadBuilder.Append(payloadData); payloadBuilder.AppendZerosTill(HidReportSize); report = _hidDevice.CreateReport(); report.Data = payloadBuilder.GetBytes(); if (!await _hidDevice.WriteReportAsync(report, HidTimeoutMs).ConfigureAwait(false)) { throw new FidoException(FidoError.InterruptedIO, "Error writing to token"); } remainingData = remainingData.Skip(HidReportSize - 5).ToArray(); seq++; } }
private async Task <byte[]> ReadResponseAsync(FidoHidMsgType msgType) { HidReport report; FidoHidMsgType recvdMsgType; do { report = await _hidDevice.ReadReportAsync(HidTimeoutMs).ConfigureAwait(false); if (report.ReadStatus != HidDeviceData.ReadStatus.Success) { throw new FidoException(FidoError.InterruptedIO, $"Error reading from token: {report.ReadStatus}"); } } while (!CheckHeader(report.Data, out recvdMsgType)); if (msgType != recvdMsgType) { throw new FidoException(FidoError.ProtocolViolation, $"received {recvdMsgType} instead of {msgType}"); } var dataLength = (report.Data[5] << 8) + report.Data[6]; var payloadData = report.Data.Skip(7).Take(Math.Min(dataLength, HidReportSize)).ToArray(); var payload = new ByteArrayBuilder(); payload.Append(payloadData); dataLength -= (int)payload.Length; var seq = 0; while (dataLength > 0) { do { report = await _hidDevice.ReadReportAsync(HidTimeoutMs).ConfigureAwait(false); if (report.ReadStatus != HidDeviceData.ReadStatus.Success) { throw new FidoException(FidoError.InterruptedIO, $"Error reading from token: {report.ReadStatus}"); } } while (!CheckHeader(report.Data, out recvdMsgType)); if ((recvdMsgType & FidoHidMsgType.CommandFlag) > 0) { throw new FidoException(FidoError.ProtocolViolation, "fragmented message continuation had command flag set"); } if ((byte)recvdMsgType != (seq & (byte)FidoHidMsgType.SequenceIdMask)) { throw new FidoException(FidoError.ProtocolViolation, $"received out-of-sequence message: Recvd:0x{(byte)recvdMsgType:X}, seq-nr:{seq}"); } seq++; payloadData = report.Data.Skip(5).Take(Math.Min(dataLength, HidReportSize)).ToArray(); dataLength -= payloadData.Length; payload.Append(payloadData); } return(payload.GetBytes()); }
protected async Task <byte[]> CallAsync(FidoHidMsgType command, byte[] data = null) { await SendRequestAsync(command, data).ConfigureAwait(false); return(await ReadResponseAsync(command).ConfigureAwait(false)); }