mirror of
				https://github.com/Ryujinx/ryuko-ng.git
				synced 2025-11-04 13:14:57 +00:00 
			
		
		
		
	yubicootp: Add signature support
Works both ways! Optional too!
This commit is contained in:
		
							parent
							
								
									490916a1ca
								
							
						
					
					
						commit
						0487031974
					
				| 
						 | 
				
			
			@ -3,6 +3,8 @@ import re
 | 
			
		|||
import config
 | 
			
		||||
import secrets
 | 
			
		||||
import asyncio
 | 
			
		||||
import base64
 | 
			
		||||
import hmac
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class YubicoOTP(Cog):
 | 
			
		||||
| 
						 | 
				
			
			@ -53,10 +55,31 @@ class YubicoOTP(Cog):
 | 
			
		|||
 | 
			
		||||
        return int("".join(hexconv), 16)
 | 
			
		||||
 | 
			
		||||
    def calc_signature(self, text):
 | 
			
		||||
        key = base64.b64decode(config.yubico_otp_secret)
 | 
			
		||||
        signature_bytes = hmac.digest(key, text.encode(), "SHA1")
 | 
			
		||||
        return base64.b64encode(signature_bytes).decode()
 | 
			
		||||
 | 
			
		||||
    def validate_response_signature(self, response_dict):
 | 
			
		||||
        yubico_signature = response_dict["h"]
 | 
			
		||||
        to_sign = ""
 | 
			
		||||
        for key in sorted(response_dict.keys()):
 | 
			
		||||
            if key == "h":
 | 
			
		||||
                continue
 | 
			
		||||
            to_sign += f"{key}={response_dict[key]}&"
 | 
			
		||||
        our_signature = self.calc_signature(to_sign.strip("&"))
 | 
			
		||||
        return our_signature == yubico_signature
 | 
			
		||||
 | 
			
		||||
    async def validate_yubico_otp(self, otp):
 | 
			
		||||
        nonce = secrets.token_hex(15)  # Random number in the valid range
 | 
			
		||||
        params = f"id={config.yubico_otp_client_id}&nonce={nonce}&otp={otp}"
 | 
			
		||||
 | 
			
		||||
        # If secret is supplied, sign our request
 | 
			
		||||
        if config.yubico_otp_secret:
 | 
			
		||||
            params += "&h=" + self.calc_signature(params)
 | 
			
		||||
 | 
			
		||||
        for api_server in self.api_servers:
 | 
			
		||||
            url = f"{api_server}/wsapi/2.0/verify?id={config.yubico_otp_client_id}&otp={otp}&nonce={nonce}"
 | 
			
		||||
            url = f"{api_server}/wsapi/2.0/verify?{params}"
 | 
			
		||||
            try:
 | 
			
		||||
                resp = await self.bot.aiosession.get(url)
 | 
			
		||||
                assert resp.status == 200
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +94,13 @@ class YubicoOTP(Cog):
 | 
			
		|||
            datafields = resptext.strip().split("\r\n")
 | 
			
		||||
            datafields = {line.split("=")[0]: line.split("=")[1] for line in datafields}
 | 
			
		||||
 | 
			
		||||
            datafields["nonce"] != nonce
 | 
			
		||||
            # Verify nonce
 | 
			
		||||
            assert datafields["nonce"] == nonce
 | 
			
		||||
 | 
			
		||||
            # Verify signature if secret is present
 | 
			
		||||
            if config.yubico_otp_secret:
 | 
			
		||||
                assert self.validate_response_signature(datafields)
 | 
			
		||||
 | 
			
		||||
            # If we got a success, then return True
 | 
			
		||||
            if datafields["status"] == "OK":
 | 
			
		||||
                return True
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -326,9 +326,9 @@ pingmods_role = 360138431524765707
 | 
			
		|||
modtoggle_role = 360138431524765707
 | 
			
		||||
 | 
			
		||||
# == Only if you want to use cogs.yubicootp ==
 | 
			
		||||
# Client ID from https://upgrade.yubico.com/getapikey/
 | 
			
		||||
# Optiona: Get your own from https://upgrade.yubico.com/getapikey/
 | 
			
		||||
yubico_otp_client_id = 1
 | 
			
		||||
# Note: You can keep client ID on 1, it will function.
 | 
			
		||||
yubico_otp_secret = ""
 | 
			
		||||
# Note: YOU CAN KEEP THIS ON 1, IT WILL STILL FUNCTION.
 | 
			
		||||
# Note: Secret is not currently used, but it's recommended for you to add
 | 
			
		||||
# if you use your own client ID so if I ever implement it, your bot won't break.
 | 
			
		||||
# Optional: If you provide a secret, requests will be signed
 | 
			
		||||
# and responses will be verified.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue