public async Task <string> CreateConnectionAsync(string counterPartyVaspId) { var counterPartyVaspCode = counterPartyVaspId.Substring(4, 8); var vaspTransportKey = await _vaspCodesService.GetTransportKeyAsync(counterPartyVaspCode); if (vaspTransportKey == null) { throw new InvalidOperationException($"Couldn't get TransportKey for vasp code {counterPartyVaspCode}"); } var sessionKey = ECDH_Key.GenerateKey(); var topic = TopicGenerator.GenerateConnectionTopic(); var privateKeyId = await _whisperRpc.RegisterKeyPairAsync(sessionKey.PrivateKey); var filter = await _whisperRpc.CreateMessageFilterAsync(topic, privateKeyId); _activeTopics.Add(topic); var connection = new Connection { Id = Guid.NewGuid().ToString("N"), Filter = filter, InboundTopic = topic, Status = ConnectionStatus.Active, CounterPartyVaspId = counterPartyVaspId, PrivateKey = sessionKey.PrivateKey, }; _connections[connection.Id] = connection; return(connection.Id); }
private async Task HandleSessionRequestAsync( string connectionId, string sessionId, string senderVaspId, string ecdhPkA) { var key = ECDH_Key.GenerateKey(); var session = new Session { Id = sessionId, Type = SessionType.Beneficiary, State = SessionState.Invited, CounterPartyVaspId = senderVaspId, ConnectionId = connectionId, CreationDateTime = DateTime.UtcNow, EcdhPrivateKey = key.PrivateKey, TempAesMessageKey = ECDH_Key.ImportKey(_privateMessageKey) .GenerateSharedSecretHex(await _vaspCodesService.GetMessageKeyAsync(senderVaspId.Substring(4))) }; if (!string.IsNullOrWhiteSpace(ecdhPkA)) { session.EstablishedAesMessageKey = key.GenerateSharedSecretHex(ecdhPkA); } _sessions[sessionId] = session; await TriggerAsyncEvent(BeneficiarySessionCreated, new BeneficiarySessionCreatedEvent { SessionId = sessionId, CounterPartyVaspId = session.CounterPartyVaspId }); }
public static VaspClient Create( VaspInformation vaspInfo, VaspContractInfo vaspContractInfo, string handshakePrivateKeyHex, string signaturePrivateKeyHex, IEthereumRpc nodeClientEthereumRpc, IWhisperRpc nodeClientWhisperRpc, IEnsProvider ensProvider, ISignService signService, ITransportClient transportClient) { var handshakeKey = ECDH_Key.ImportKey(handshakePrivateKeyHex); var vaspClient = new VaspClient( handshakeKey, signaturePrivateKeyHex, vaspContractInfo, vaspInfo, nodeClientEthereumRpc, nodeClientWhisperRpc, ensProvider, transportClient, signService); return(vaspClient); }
public async Task <string> CreateSessionAndSendSessionRequestAsync(string counterPartyVaspId) { var session = new Session { Id = Guid.NewGuid().ToString("N"), Type = SessionType.Originator, State = SessionState.Created, CounterPartyVaspId = counterPartyVaspId, CreationDateTime = DateTime.UtcNow, EcdhPrivateKey = ECDH_Key.GenerateKey().PrivateKey, }; _sessions[session.Id] = session; var whisperConnection = await _transportService.CreateConnectionAsync(session.CounterPartyVaspId); session.ConnectionId = whisperConnection; var messageKey = await _vaspCodesService.GetMessageKeyAsync(session.CounterPartyVaspId.Substring(4)); session.TempAesMessageKey = ECDH_Key.ImportKey(_privateMessageKey).GenerateSharedSecretHex(messageKey); await SendMessageAsync( session, MessageType.SessionRequest, Instruction.Invite, ECDH_Key.ImportKey(session.EcdhPrivateKey).PublicKey, new SessionRequest()); return(session.Id); }
private async Task HandleAcceptMessageAsync(OpenVaspPayload payload) { var connection = _connections[payload.ConnectionId]; if (connection == null) { throw new ArgumentException($"Connection with id '{payload.ConnectionId}' was not found"); } var sharedSecret = ECDH_Key.ImportKey(connection.PrivateKey).GenerateSharedSecretHex(payload.EcdhPk); (connection.Filter, connection.SymKeyId) = await RegisterConnectionAsync(connection.InboundTopic, sharedSecret); connection.SharedPrivateEncryptionKey = sharedSecret; connection.OutboundTopic = payload.ReturnTopic; await AcknowledgeMessageAsync( payload, connection.SymKeyId, null); var senderVaspCode = payload.SenderVaspId.Substring(4, 8); var signingKey = await _vaspCodesService.GetSigningKeyAsync(senderVaspCode); await TriggerAsyncEvent(TransportMessageReceived, new TransportMessageEvent { ConnectionId = payload.ConnectionId, SenderVaspId = payload.SenderVaspId, Instruction = payload.Instruction, Payload = payload.OvMessage, Timestamp = DateTime.UtcNow, SigningKey = signingKey }); }
public void ValidateEcdhKeysSharedKeyGenerationForPrivateKeyTest(string aliceKeyStr, string bobKeyStr, string expectedSharedStr) { BigInteger aliceKey = new BigInteger(aliceKeyStr.Replace("L", "")); BigInteger bobKey = new BigInteger(bobKeyStr.Replace("L", "")); var aliceConverted = aliceKey.ToByteArray().ToHex(true); var bobConverted = bobKey.ToByteArray().ToHex(true); ECDH_Key alice = ECDH_Key.ImportKey(aliceConverted); ECDH_Key bob = ECDH_Key.ImportKey(bobConverted); var shared1 = alice.GenerateSharedSecretHex(bob.PublicKey); var shared2 = bob.GenerateSharedSecretHex(alice.PublicKey); var res = new BigInteger(shared1.HexToByteArray()).ToString(); Assert.Equal(shared1, shared2); Assert.Equal(expectedSharedStr.Replace("L", ""), res); testOutputHelper.WriteLine(shared1); testOutputHelper.WriteLine(shared2); testOutputHelper.WriteLine(res); EthECKey aliceKeyEth = new EthECKey(aliceConverted); EthECKey bobKeyEth = new EthECKey(bob.PublicKey.HexToByteArray(), false); var shared3 = aliceKeyEth.CalculateCommonSecret(bobKeyEth).ToHex(true); testOutputHelper.WriteLine(shared3); Assert.Equal(shared1, shared3); }
public void ValidateEcdhKeysSharedKeyGenerationForPubKeyTest(string privateKeySecp256k1, string publicKeySecp256k1, string sharedKey) { ECDH_Key alice = ECDH_Key.ImportKey(privateKeySecp256k1); var shared1 = alice.GenerateSharedSecretHex(publicKeySecp256k1); Assert.Equal(sharedKey, shared1); }
public void GenerateHandshakeKeyForVaspSmartContractTest() { ECDH_Key alice = ECDH_Key.GenerateKey(); ECDH_Key importValid = ECDH_Key.ImportKey(alice.PrivateKey); Assert.Equal(alice.PrivateKey, importValid.PrivateKey); Assert.Equal(alice.PublicKey, importValid.PublicKey); }
private async Task StartAsync() { var transportKey = ECDH_Key.ImportKey(_privateTransportKey); var privateKeyId = await _whisperRpc.RegisterKeyPairAsync(transportKey.PrivateKey); var filter = await _whisperRpc.CreateMessageFilterAsync(_vaspId.Substring(4), privateKeyId); _filter = filter; _timer.Start(); }
public void SharedSecretECDHGenerationTest() { ECDH_Key alice = ECDH_Key.GenerateKey(); ECDH_Key bob = ECDH_Key.GenerateKey(); var shared1 = alice.GenerateSharedSecretHex(bob.PublicKey); var shared2 = bob.GenerateSharedSecretHex(alice.PublicKey); Assert.Equal(shared1, shared2); }
public void GenerateHandshakeKeyForVaspSmartContractTest() { ECDH_Key alice = ECDH_Key.GenerateKey(); ECDH_Key importValid = ECDH_Key.ImportKey(alice.PrivateKey); Assert.Equal(alice.PrivateKey, importValid.PrivateKey); Assert.Equal(alice.PublicKey, importValid.PublicKey); testOutputHelper.WriteLine(alice.PrivateKey.HexToByteArray().Length.ToString()); testOutputHelper.WriteLine(alice.PrivateKey); testOutputHelper.WriteLine(alice.PublicKey); }
public void SharedSecretECDHImportTest() { EthECKey ecKey1 = EthECKey.GenerateKey(); EthECKey ecKey2 = EthECKey.GenerateKey(); ECDH_Key alice = ECDH_Key.ImportKey(ecKey1.GetPrivateKey()); ECDH_Key bob = ECDH_Key.ImportKey(ecKey2.GetPrivateKey()); var ecPubKey1 = ecKey1.GetPubKey().ToHex(true); var ecPubKey2 = ecKey2.GetPubKey().ToHex(true); var shared1 = alice.GenerateSharedSecretHex(bob.PublicKey); var shared2 = bob.GenerateSharedSecretHex(alice.PublicKey); Assert.Equal(shared1, shared2); }
public async Task SessionReplyAsync( string sessionId, SessionReplyMessageCode code) { var session = _sessions[sessionId]; await SendMessageAsync( session, MessageType.SessionReply, code == SessionReplyMessageCode.SessionAccepted?Instruction.Accept : Instruction.Deny, ECDH_Key.ImportKey(session.EcdhPrivateKey).PublicKey, new SessionReply { Code = ((int)code).ToString() }); }
public void ValidateEcdhKeysSharedKeyGenerationForPrivateKeyTest(string aliceKeyStr, string bobKeyStr, string expectedSharedStr) { BigInteger aliceKey = new BigInteger(aliceKeyStr.Replace("L", "")); BigInteger bobKey = new BigInteger(bobKeyStr.Replace("L", "")); var aliceConverted = aliceKey.ToByteArray().ToHex(true); var bobConverted = bobKey.ToByteArray().ToHex(true); ECDH_Key alice = ECDH_Key.ImportKey(aliceConverted); ECDH_Key bob = ECDH_Key.ImportKey(bobConverted); var shared1 = alice.GenerateSharedSecretHex(bob.PublicKey); var shared2 = bob.GenerateSharedSecretHex(alice.PublicKey); var res = new BigInteger(shared1.HexToByteArray()).ToString(); Assert.Equal(shared1, shared2); Assert.Equal(expectedSharedStr.Replace("L", ""), res); }
/// <summary> /// Create a session and send a request session message to beneficiary Vasp. /// </summary> /// <param name="originator">Information about a client who sends a transfer</param> /// <param name="beneficiaryVaan">Information about a receiver of the transfer</param> /// <returns>OriginatorSession through which transfer request and transfer dispatch should be requested.</returns> public async Task <OriginatorSession> CreateSessionAsync( Originator originator, VirtualAssetsAccountNumber beneficiaryVaan, IOriginatorVaspCallbacks _originatorVaspCallbacks) { string counterPartyVaspContractAddress = await _ensProvider.GetContractAddressByVaspCodeAsync(beneficiaryVaan.VaspCode); var contractInfo = await _ethereumRpc.GetVaspContractInfoAync(counterPartyVaspContractAddress); var sessionKey = ECDH_Key.GenerateKey(); var sharedKey = sessionKey.GenerateSharedSecretHex(contractInfo.HandshakeKey); var session = new OriginatorSession( originator, this._vaspContractInfo, this.VaspInfo, beneficiaryVaan, contractInfo.SigningKey, contractInfo.HandshakeKey, sharedKey, sessionKey.PublicKey, this._signatureKey, _whisperRpc, _transportClient, _signService, _originatorVaspCallbacks); if (_originatorSessionsDict.TryAdd(session.SessionId, session)) { this.NotifySessionCreated(session); session.OnSessionTermination += this.ProcessSessionTermination; await session.StartAsync(); return(session); } await session.TerminateAsync(TerminationMessage.TerminationMessageCode .SessionClosedTransferCancelledByOriginator); //TODO: process it as exception or retry return(null); }
//TODO: Get rid of Whisper completely private VaspClient( ECDH_Key handshakeKey, string signatureHexKey, VaspContractInfo vaspContractInfo, VaspInformation vaspInfo, IEthereumRpc nodeClientEthereumRpc, IWhisperRpc nodeClientWhisperRpc, IEnsProvider ensProvider, ITransportClient transportClient, ISignService signService) { this._handshakeKey = handshakeKey; this._signatureKey = signatureHexKey; this._vaspContractInfo = vaspContractInfo; this.VaspInfo = vaspInfo; this._ethereumRpc = nodeClientEthereumRpc; this._whisperRpc = nodeClientWhisperRpc; this._cancellationTokenSource = new CancellationTokenSource(); this._ensProvider = ensProvider; this._transportClient = transportClient; this._signService = signService; }
public async Task HandleSessionReplyAsync(string sessionId, string messageCode, string ecdhPkB) { var session = _sessions[sessionId]; if (session.State != SessionState.Initiated) { _logger?.LogWarning( $"Can't process reply for session {sessionId} that is in {session.State} state. Skipping."); return; } var sessionReplyCode = (SessionReplyMessageCode)Enum.Parse(typeof(SessionReplyMessageCode), messageCode); session.State = sessionReplyCode == SessionReplyMessageCode.SessionAccepted ? SessionState.Open : SessionState.Declined; if (!string.IsNullOrWhiteSpace(ecdhPkB)) { session.EstablishedAesMessageKey = ECDH_Key.ImportKey(session.EcdhPrivateKey).GenerateSharedSecretHex(ecdhPkB); } if (session.State == SessionState.Open) { await TriggerAsyncEvent(OriginatorSessionApproved, new OriginatorSessionApprovedEvent { SessionId = sessionId }); } else { await TriggerAsyncEvent(OriginatorSessionDeclined, new OriginatorSessionDeclinedEvent { SessionId = sessionId }); } }
public async Task CreateVaspForBank() { var signature = new EthECKey(Settings.PersonSignaturePrivateKeyHex); var handshake = ECDH_Key.ImportKey(Settings.PersonHandshakePrivateKeyHex); var signPub = signature.GetPubKey().ToHex(prefix: true); var handshakePub = handshake.PublicKey; (VaspInformation vaspInfo, VaspContractInfo vaspContractInfo) = await VaspInformationBuilder.CreateForBankAsync( NodeClient.EthereumRpc, Settings.VaspSmartContractAddressBank, Settings.Bic); VaspClient vasp = VaspClient.Create( vaspInfo, vaspContractInfo, Settings.BankHandshakePrivateKeyHex, Settings.BankSignaturePrivateKeyHex, NodeClient.EthereumRpc, NodeClient.WhisperRpc, _fakeEnsProvider, _signService, NodeClient.TransportClient); // VASP paramaters must be derived from smart contract Assert.NotNull(vasp.VaspInfo.Name); Assert.NotNull(vasp.VaspInfo.VaspIdentity); Assert.NotNull(vasp.VaspInfo.PostalAddress); // VASP natural person parameters should be same what we pass in constructor Assert.Equal(vasp.VaspInfo.BIC, Settings.Bic); Assert.Null(vasp.VaspInfo.NaturalPersonIds); Assert.Null(vasp.VaspInfo.PlaceOfBirth); Assert.Null(vasp.VaspInfo.JuridicalPersonIds); }
private async Task TransportClientOnTransportMessageReceived(TransportMessageEvent evt) { MessageContent messageContent; string messagePlaintext; string sig; var session = _sessions.Values.SingleOrDefault(x => x.ConnectionId == evt.ConnectionId); if (session == null) { var messageKey = await _vaspCodesService.GetMessageKeyAsync(evt.SenderVaspId.Substring(4)); var aesMessageKey = ECDH_Key.ImportKey(_privateMessageKey).GenerateSharedSecretHex(messageKey); (messageContent, messagePlaintext, sig) = _messageFormatterService.Deserialize(evt.Payload, aesMessageKey, evt.SigningKey); } else if (string.IsNullOrWhiteSpace(session.EstablishedAesMessageKey)) { (messageContent, messagePlaintext, sig) = _messageFormatterService.Deserialize(evt.Payload, session.TempAesMessageKey, evt.SigningKey); } else { try { (messageContent, messagePlaintext, sig) = _messageFormatterService.Deserialize(evt.Payload, session.EstablishedAesMessageKey, evt.SigningKey); } catch (InvalidCipherTextException) { (messageContent, messagePlaintext, sig) = _messageFormatterService.Deserialize(evt.Payload, session.TempAesMessageKey, evt.SigningKey); } } switch (evt.Instruction) { case Instruction.Invite: await HandleSessionRequestAsync( evt.ConnectionId, messageContent.Header.SessionId, evt.SenderVaspId, messageContent.Header.EcdhPk); break; case Instruction.Accept: case Instruction.Deny: var messageCode = messageContent.RawBody.ToObject <SessionReply>().Code; await HandleSessionReplyAsync( messageContent.Header.SessionId, messageCode, messageContent.Header.EcdhPk); break; case Instruction.Close: switch (messageContent.Header.MessageType) { case MessageType.Abort: await HandleSessionAbortAsync( messageContent.Header.SessionId, messageContent.RawBody.ToObject <SessionAbort>().Code); break; case MessageType.Termination: await HandleTerminationAsync(messageContent.Header.SessionId); break; } break; case Instruction.Update: await TriggerAsyncEvent(ApplicationMessageReceived, new ApplicationMessageReceivedEvent { Type = messageContent.Header.MessageType, SessionId = session.Id, Payload = messageContent.RawBody }); break; default: throw new NotSupportedException( $"Instruction type {Enum.GetName(typeof(Instruction), evt.Instruction)} is not supported"); } }
public async Task SendAsync(string connectionId, string message, Instruction instruction, string receiverVaspId) { var connection = _connections[connectionId]; var receiverVaspCode = (receiverVaspId ?? connection.CounterPartyVaspId).Substring(4, 8); var envelopeId = Guid.NewGuid().ToString("N"); var payload = new OpenVaspPayload( instruction, _vaspId, connection.Id, envelopeId) { ReturnTopic = connection.InboundTopic, OvMessage = message }; if (instruction == Instruction.Invite || instruction == Instruction.Accept || instruction == Instruction.Deny) { payload.EcdhPk = ECDH_Key.ImportKey(connection.PrivateKey).PublicKey; } var topic = connection.OutboundTopic ?? receiverVaspCode; if (string.IsNullOrWhiteSpace(topic)) { throw new InvalidOperationException($"Topic is empty for connection {connection.Id}"); } var envelope = new MessageEnvelope { Topic = topic, }; if (instruction == Instruction.Invite || instruction == Instruction.Close && string.IsNullOrWhiteSpace(connection.SymKeyId)) { envelope.EncryptionType = EncryptionType.Asymmetric; var vaspTransportKey = await _vaspCodesService.GetTransportKeyAsync(receiverVaspCode); if (vaspTransportKey == null) { throw new InvalidOperationException($"Transport key for vasp code {receiverVaspCode} cannot be found during message sending"); } envelope.EncryptionKey = vaspTransportKey.DecompressPublicKey().ToHex(true); } else if (instruction == Instruction.Accept || instruction == Instruction.Deny || instruction == Instruction.Close && connection.Status == ConnectionStatus.PartiallyActive) { envelope.EncryptionType = EncryptionType.Asymmetric; envelope.EncryptionKey = connection.CounterPartyPublicKey.DecompressPublicKey().ToHex(true); } else { envelope.EncryptionType = EncryptionType.Symmetric; envelope.EncryptionKey = connection.SymKeyId; } var outboundEnvelope = new OutboundEnvelope { Id = envelopeId, ConnectionId = connectionId, Envelope = envelope, TotalResents = 0, Payload = payload.ToString() }; _openVaspPayloads[payload.EnvelopeId] = payload; await _outboundEnvelopeService.SendEnvelopeAsync(outboundEnvelope, instruction != Instruction.Deny); if (instruction == Instruction.Deny) { await DeactivateAsync(connectionId); } }
private async Task HandleInviteMessageAsync(OpenVaspPayload payload) { var senderVaspCode = payload.SenderVaspId.Substring(4, 8); var vaspTransportKey = await _vaspCodesService.GetTransportKeyAsync(senderVaspCode); if (vaspTransportKey == null) { _logger?.LogError($"Transport key for vasp code {senderVaspCode} cannot be found during invitation processing"); return; } _connections.TryGetValue(payload.ConnectionId, out var connection); if (connection != null) { bool isSameData = connection.CounterPartyVaspId == payload.SenderVaspId && connection.OutboundTopic == payload.ReturnTopic; if (isSameData) { _logger?.LogWarning( $"Received invite for already existing connectionId {payload.ConnectionId} with the same data. Skipping."); await AcknowledgeMessageAsync( payload, null, payload.EcdhPk.DecompressPublicKey().ToHex(true)); } else { _logger?.LogWarning( $"Received invite for already existing connectionId {payload.ConnectionId} with the different data:{Environment.NewLine}" + $"SenderVaspId: {connection.CounterPartyVaspId} - {payload.SenderVaspId},{Environment.NewLine}" + $"Topic: {connection.OutboundTopic} - {payload.ReturnTopic},{Environment.NewLine}"); } return; } var topic = TopicGenerator.GenerateConnectionTopic(); var sessionKey = ECDH_Key.GenerateKey(); var sharedSecret = sessionKey.GenerateSharedSecretHex(payload.EcdhPk); var(filter, symKeyId) = await RegisterConnectionAsync(topic, sharedSecret); var newConnection = new Connection { Id = payload.ConnectionId, Filter = filter, InboundTopic = topic, OutboundTopic = payload.ReturnTopic, Status = ConnectionStatus.PartiallyActive, CounterPartyVaspId = payload.SenderVaspId, SymKeyId = symKeyId, SharedPrivateEncryptionKey = sharedSecret, PrivateKey = sessionKey.PrivateKey, CounterPartyPublicKey = payload.EcdhPk, }; _connections[newConnection.Id] = newConnection; await AcknowledgeMessageAsync( payload, null, payload.EcdhPk.DecompressPublicKey().ToHex(true)); var signingKey = await _vaspCodesService.GetSigningKeyAsync(senderVaspCode); await TriggerAsyncEvent(TransportMessageReceived, new TransportMessageEvent { ConnectionId = payload.ConnectionId, SenderVaspId = payload.SenderVaspId, Instruction = payload.Instruction, Payload = payload.OvMessage, Timestamp = DateTime.UtcNow, SigningKey = signingKey }); }