Kioubit Authentication Services
Used by the Kioubit autopeering System: https://dn42.g-load.eu/auth/
The Kioubit Verify Services allow you to verify that a user is the holder of an ASN based on the authentication
methods the user has provided to the dn42 registry.
Before using this API you will need to contact Kioubit to have your domain enabled. Use the 'localhost' domain for testing purposes
1. Add the Kioubit Authentication Button to your website
<form action="https://dn42.g-load.eu/auth/">
<link rel="stylesheet" href="auth.css">
<input type="hidden" name="return" value="https://example.org/">
<button type="submit" class="kioubit-btn-dark"><img width="35" height="35" type="image/svg+xml" src="auth.svg"
class="kioubit-btn-logo">Authenticate with Kioubit.dn42</button>
</form>
Replace "https://example.org/ "
with your website where the
code from step 2
is hosted
Download the styles from https://dn42.g-load.eu/auth/assets/auth-button.zip
2. Add the following verification code to your website
PHP example code
<?php

// Get parameters and signature
$params = filter_var($_GET["params"], FILTER_SANITIZE_URL);
$signature = filter_var($_GET["signature"], FILTER_SANITIZE_URL);

// Signature Verification
$public_key_pem = file_get_contents('public_key.pem');
$r = openssl_verify($params, base64_decode($signature), $public_key_pem, OPENSSL_ALGO_SHA512);
if ($r != 1) {
 die("Could not verify signature");
}

// Decode parameters to json
$params = base64_decode($params);
$info = json_decode($params, true);

// Check that the request is for our own domain
if ("owndomain.com" != $info["domain"]) {
 die("The request is for a different domain");
}

// Check the time of the request
if (abs($info["time"] - time()) > 60) {
 die("The request has expired");
}

// Print json object
echo nl2br(print_r($info, true));

?>
Python example code
#!/usr/bin/env python3

import base64
import os
import json
import time
# Requires pyca/cryptography (cryptography==43.0.1)
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.exceptions import InvalidSignature


class AuthVerifier():
 PUBKEY_FILE = os.path.dirname(__file__)+"/public_key.pem"
 __domain: str = ""
 __pubKey: ec.EllipticCurvePublicKey | None = None

 def __init__(self, domain: str, pubkey: str = PUBKEY_FILE):
 self.__domain = domain
 with open(pubkey, "br") as pk:
 pk_content = pk.read()
 self.__pubKey = serialization.load_pem_public_key(pk_content)

 def verify(self, params: str, signature: str) -> tuple[bool, dict[str, any] | str]:
 try:
 sig = base64.b64decode(signature)
 except base64.binascii.Error:
 return False, "Invalid base64 encoding for 'signature'"
 try:
 self.__pubKey.verify(sig, params.encode(), ec.ECDSA(hashes.SHA512()))
 except InvalidSignature:
 return False, "Invalid signature"

 try:
 params_decoded = base64.b64decode(params)
 except base64.binascii.Error:
 return False, "Invalid base64 encoding for 'params'"

 try:
 user_data = json.loads(params_decoded)
 if abs(time.time() - user_data["time"]) > 60:
 return False, "Signature has expired"
 if user_data["domain"] != self.__domain.replace("https://",""):
 return False, "Invalid domain"
 except json.decoder.JSONDecodeError:
 return False, "Invalid user data JSON"
 except KeyError:
 return False, "Required value not found in JSON"
 return True, user_data


if __name__ == "__main__":
 test_params="eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI2NjkyNiwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0="
 test_signature="MIGIAkIBAmwz3sQ1vOkH8+8e0NJ8GsUqKSaazIWmYDp60sshlTo7gCAopZOZ6/+tD6s+oEGM1i5mKGbHgK9ROATQLHxUZecCQgCa2N828uNn76z1Yg63/c7veMVIiK4l1X9TCUepJnJ3mCto+7ogCP+2vQm6GHipSNRF4wnt6tZbir0HZvrqEnRAmA=="
 
 from unittest import mock
 example_verifier = AuthVerifier("svc.burble.dn42")

 @mock.patch('time.time', mock.MagicMock(return_value=1668266926))
 def test_with_correct_time():
 result, user_data = example_verifier.verify(params=test_params, signature=test_signature)
 print(user_data)
 assert result == True
 
 @mock.patch('time.time', mock.MagicMock(return_value=1668266910))
 def test_with_backwards_time():
 result, _ = example_verifier.verify(params=test_params, signature=test_signature)
 assert result == True

 @mock.patch('time.time', mock.MagicMock(return_value=1668266000))
 def test_with_expired_time():
 result, _ = example_verifier.verify(params=test_params, signature=test_signature)
 assert result == False

 test_with_correct_time()
 test_with_backwards_time()
 test_with_expired_time()
GO example code
package main

import (
	"crypto/ecdsa"
	"crypto/sha512"
	"crypto/x509"
	_ "embed"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"math"
	"time"
)

//go:embed public_key.pem
var pemPubKey []byte

const myDomain = "svc.burble.dn42"

func VerifyAuthToken(signature, params string) (user_data map[string]any, err error) {
	// Read public key
	blockPub, _ := pem.Decode(pemPubKey)
	x509EncodedPub := blockPub.Bytes
	genericPublicKey, err := x509.ParsePKIXPublicKey(x509EncodedPub)
	if err != nil {
		return nil, errors.New("internal server error")
	}
	publicKey := genericPublicKey.(*ecdsa.PublicKey)

	// Hash parameters
	hash := sha512.Sum512([]byte(params))

	// Decode base64 signature
	signatureBytes, err := base64.StdEncoding.DecodeString(signature)
	if err != nil {
		return nil, errors.New("failed to decode signature")
	}

	// Verify signature
	if !ecdsa.VerifyASN1(publicKey, hash[:], signatureBytes) {
		return nil, errors.New("invalid signature")
	}

	// Decode parameters
	parameterBytes, err := base64.StdEncoding.DecodeString(params)
	if err != nil {
		return nil, fmt.Errorf("failed decoding verified parameters: %w", err)
	}

	err = json.Unmarshal(parameterBytes, &user_data)
	if err != nil {
		return nil, fmt.Errorf("failed unmarshaling verified parameters: %w", err)
	}

	if math.Abs(user_data["time"].(float64)-float64(time.Now().Unix())) > 60 {
		return nil, errors.New("the request has expired")
	}

	if user_data["domain"].(string) != myDomain {
		return nil, errors.New("this request is for a different domain")
	}

	return
}

func main() {
	var test_params = "eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI2NjkyNiwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0="
	var test_signature = "MIGIAkIBAmwz3sQ1vOkH8+8e0NJ8GsUqKSaazIWmYDp60sshlTo7gCAopZOZ6/+tD6s+oEGM1i5mKGbHgK9ROATQLHxUZecCQgCa2N828uNn76z1Yg63/c7veMVIiK4l1X9TCUepJnJ3mCto+7ogCP+2vQm6GHipSNRF4wnt6tZbir0HZvrqEnRAmA=="
	fmt.Println(VerifyAuthToken(test_signature, test_params))
}
3. Download Kioubit's Public Key required for the verification code
Download the public key from: https://dn42.g-load.eu/auth/assets/public_key.pem and place the
key in the same directory as the PHP script (or any other kind of script).
$ openssl pkey -pubin -in public_key.pem -text -noout
Public-Key: (521 bit)
pub:
04:00:e0:30:8e:95:1e:20:f7:8b:9d:2a:fb:bd:fb:
32:9b:c2:ac:55:fe:b8:e0:ee:0f:c8:8f:dd:53:9c:
4a:94:84:80:62:dc:b0:63:06:8c:a4:71:21:67:58:
1b:57:82:79:ab:8f:45:b1:06:de:2f:02:3e:d8:3b:
98:c2:41:92:4c:55:e2:00:2c:27:12:16:49:f8:e3:
b6:ec:a7:a3:5d:43:3c:53:31:0f:14:61:33:d7:fe:
0e:c0:cf:11:03:39:5b:57:7d:20:68:fc:18:99:17:
d2:d8:3e:69:b4:cf:09:3e:cb:b6:66:47:c2:9f:d3:
68:44:b4:96:db:8c:df:c6:a0:3e:8c:ab:57
ASN1 OID: secp521r1
NIST CURVE: P-521
Example data provided by the API
{
 "asn": "4242423914",
 "time": 1726845156,
 "allowed4": [
 "172.20.14.32/27",
 "172.20.53.96/27"
 ],
 "allowed6": [
 "fdcf:8538:9ad5::/48",
 "fdfc:e23f:fb45:3234::/64"
 ],
 "mnt": [
 "KIOUBIT-MNT"
 ],
 "effective_mnt": "KIOUBIT-MNT",
 "domain": "dn42.g-load.eu",
 "authtype": "email"
}
Notes about some fields:
allowed4 & allowed6: The owned prefixes for all maintainers of an ASN
mnt: An array of all maintainers of an ASN
effective_mnt: The maintainer that authenticated
authtype: The authentication method used