Jump to content
Not connected, Your IP: 216.73.216.189

Recommended Posts

Posted ... (edited)

Hi guys I'm a new user I just joined not too long ago. I tried making a comment yesterday for some reason it disappeared? There were some speed issues with the servers but they subsided.

Anyways I was taking a look at the code (hope that's okay) and saw the following:
/src/Lib.Core/Providers/Service.cs

First you say when you're bootstrapping directly via IP you can't use TLS but surely there must be some other way to encrypt the tunnel itself instead of manually fiddling with the data? Then you use base64 to encode the modulus and exponent and go on as to manually build up the key instead of using a PEM file or the like, then you manually encrypt the data and fiddle around with the string? I don't really get the thought process behind this. Why not just register a domain then or simply use some other means of transferring the data?

Why not just take the data (username / password for example) and manually encrypt it with a public key (imported via a PEM file) and transfer it like that if your not using TLS? Why make this so complicated?

Could you please also lift the posting restrictions for paid users? Apparently you can only post once a day and your comments need to be manually approved. I get that you need to take measures to prevent spam but for paid users surely there can be some more restrictions? I saw a few interesting posts yesterday but couldn't comment on them because of this.

// Intentional: AirVPN bootstrap uses HTTP without TLS; payload is AES-256 with RSA-wrapped session key (see below).
// Direct IP bootstrap URLs cannot rely on TLS common-name validation.

// 'S' is the AES 256 bit one-time session key, crypted with a RSA 4096 public-key.
// 'D' is the data from the client to our server, crypted with the AES.
// The server answer is XML decrypted with the same AES session.
public static byte[] AssocToUtf8Bytes(Dictionary<string, string> assoc)
		{
			string output = "";
			foreach (KeyValuePair<string, string> kp in assoc)
			{
				output += ExtensionsString.Base64Encode(kp.Key.GetUtf8Bytes()) + ":" + ExtensionsString.Base64Encode(kp.Value.GetUtf8Bytes()) + "\n";
			}
			return System.Text.Encoding.UTF8.GetBytes(output);
		}

		public static byte[] AssocToUtf8Bytes(Dictionary<string, byte[]> assoc)
		{
			string output = "";
			foreach (KeyValuePair<string, byte[]> kp in assoc)
			{
				output += ExtensionsString.Base64Encode(kp.Key.GetUtf8Bytes()) + ":" + ExtensionsString.Base64Encode(kp.Value) + "\n";
			}
			return System.Text.Encoding.UTF8.GetBytes(output);
		}
 
		public XmlDocument FetchUrl(string url, Dictionary<string, string> parameters)
		{
			using (Aes aes = Aes.Create())
			{
				aes.KeySize = 256;
				aes.GenerateKey();
				aes.GenerateIV();

				// Generate S					
				string rsaModulus = "";
				string rsaExponent = "";

				if (Manifest.GetAttributeString("auth_rsa_modulus", "") != "")
				{
					rsaModulus = Manifest.GetAttributeString("auth_rsa_modulus", "");
					rsaExponent = Manifest.GetAttributeString("auth_rsa_exponent", "");
				}
				else // Compatibility <2.21.0
				{
					rsaModulus = Manifest.SelectSingleNode("rsa/RSAParameters/Modulus").InnerText;
					rsaExponent = Manifest.SelectSingleNode("rsa/RSAParameters/Exponent").InnerText;
				}

				RSAParameters publicKey = new RSAParameters();

				publicKey.Modulus = Convert.FromBase64String(rsaModulus);
				publicKey.Exponent = Convert.FromBase64String(rsaExponent);

				Dictionary<string, byte[]> assocParamS = new Dictionary<string, byte[]>();
				assocParamS["key"] = aes.Key;
				assocParamS["iv"] = aes.IV;

				byte[] bytesParamS = null;
				using (RSACryptoServiceProvider csp = new RSACryptoServiceProvider())
				{
					csp.ImportParameters(publicKey);
					bytesParamS = csp.Encrypt(AssocToUtf8Bytes(assocParamS), false);
				}

				// Generate D

				byte[] aesDataIn = AssocToUtf8Bytes(parameters);
				byte[] bytesParamD = null;

				{
					MemoryStream aesCryptStream = null;
					CryptoStream aesCryptStream2 = null;

					try
					{
						aesCryptStream = new MemoryStream();
						using (ICryptoTransform aesEncryptor = aes.CreateEncryptor())
						{
							aesCryptStream2 = new CryptoStream(aesCryptStream, aesEncryptor, CryptoStreamMode.Write);
							aesCryptStream2.Write(aesDataIn, 0, aesDataIn.Length);
							aesCryptStream2.FlushFinalBlock();

							bytesParamD = aesCryptStream.ToArray();
						}
					}
					finally
					{
						if (aesCryptStream2 != null)
							aesCryptStream2.Dispose();
						else if (aesCryptStream != null)
							aesCryptStream.Dispose();
					}
				}
Edited ... by darkocean69

Share this post


Link to post
@darkocean69

Hello!

Good question. In 2010 we wanted an encrypted stream without HTTPS and/or certificates that looked as much as possible as plain HTTP for good reasons. We also wanted to avoid importing public keys and make the procedure immediately usable on the software. The method was good enough to survive for 16 years. Now we can also implement potentially bootstrap server access via HTTPS or more esotic ways but there's really no need at the moment, we'll see.

Kind regards
 

Share this post


Link to post
On 7/22/2026 at 4:59 PM, bytewalker said:

Could you please also lift the posting restrictions for paid users? Apparently you can only post once a day and your comments need to be manually approved. I get that you need to take measures to prevent spam but for paid users surely there can be some more restrictions? I saw a few interesting posts yesterday but couldn't comment on them because of this.


They will be lifted automatically after a certain small amount of approved posts, just keep posting. :)

NOT AN AIRVPN TEAM MEMBER. USE TICKETS FOR PROFESSIONAL SUPPORT.

LZ1's New User Guide to AirVPN « Plenty of stuff for advanced users, too!

Want to contact me directly? All relevant methods are on my About me page.

Share this post


Link to post
Posted ... (edited)
On 7/24/2026 at 6:49 AM, Tech Jedi Alex said:

They will be lifted automatically after a certain small amount of approved posts, just keep posting. :)

Ok thanks.

Did you help write the client? I've been trying on and off to write my own client but have been experiencing some odd behaviour when querying the API. When I call it with parameters
"act" => "connect"
same parameters as the Eddie client but somehow it instead returns
<?xml version="1.0" standalone="yes"?>
<connect message="" message_action="stop"/>
I haven't dug deep into the code yet mainly intercepting the API requests and interpreting them myself and reading and logging the main functions to see what's going on, what's being sent etc, matching that.

For some reason the AES key is also regenerated and a different one is used every single request? Instead of per session or the like.

When I call manifest and login and such I get the expected results but not connect, although I'm not quite certain if I even need it or if I'm better to skip over it. I mean when you call login it responds back with the certs, public key, etc... everything you need to establish a connection, I guess you could just call manifest the API for configs and using that connect.

Maybe I'm going about this the wrong way tho, technically you could just use the API key and leave it at that, call the manifest or whatever to get all available servers, pick one, generate a config then call wireguard (or openvpn) and create a tunnel. I think I'll maybe try that for now. Of course you can't via the client with your username and password that way. I think I'll start with that maybe add the login feature later when I get something working.

Addition: 
I logged the response which I get back in the original Eddie client and get the same outcome as in my program, so it's not an error of mine calling connect returns
❯ cat /tmp/eddie_response_xml
<?xml version="1.0" standalone="yes"?>
<connect message="" message_action="stop"/>

regardless, can someone clarify why? What exactly is the point of this, and I thought message_action="stop" generally was for error messages? Why let the server know you connect to it to then return an empty string?

I guess this has to do with https://airvpn.org/api/disconnect but why not return an approval message that the server registered your request?

Think I'll skip this one and implement it later, for now I'll just keep the login and start working on allow the client to connect.


  Edited ... by bytewalker

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Security Check
    Play CAPTCHA Audio
    Refresh Image

×
×
  • Create New...