mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-05 05:47:10 -04:00
feat: add SIP authentication
change icon
This commit is contained in:
parent
d3b32cc14e
commit
362434cd29
8 changed files with 166 additions and 2 deletions
30
components.d.ts
vendored
30
components.d.ts
vendored
|
@ -89,17 +89,28 @@ declare module '@vue/runtime-core' {
|
||||||
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
|
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
|
||||||
IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
|
IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
|
||||||
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
|
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
|
||||||
|
'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
|
||||||
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
|
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
|
||||||
|
IconMdiArrowDown: typeof import('~icons/mdi/arrow-down')['default']
|
||||||
|
IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default']
|
||||||
|
IconMdiCamera: typeof import('~icons/mdi/camera')['default']
|
||||||
IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
|
IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
|
||||||
IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
|
IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
|
||||||
IconMdiClose: typeof import('~icons/mdi/close')['default']
|
IconMdiClose: typeof import('~icons/mdi/close')['default']
|
||||||
IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
|
IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
|
||||||
|
IconMdiDeleteOutline: typeof import('~icons/mdi/delete-outline')['default']
|
||||||
|
IconMdiDownload: typeof import('~icons/mdi/download')['default']
|
||||||
IconMdiEye: typeof import('~icons/mdi/eye')['default']
|
IconMdiEye: typeof import('~icons/mdi/eye')['default']
|
||||||
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
|
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
|
||||||
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
|
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
|
||||||
|
IconMdiPause: typeof import('~icons/mdi/pause')['default']
|
||||||
|
IconMdiPlay: typeof import('~icons/mdi/play')['default']
|
||||||
|
IconMdiRecord: typeof import('~icons/mdi/record')['default']
|
||||||
|
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||||
IconMdiSearch: typeof import('~icons/mdi/search')['default']
|
IconMdiSearch: typeof import('~icons/mdi/search')['default']
|
||||||
IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
|
IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
|
||||||
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
|
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
|
||||||
|
IconMdiVideo: typeof import('~icons/mdi/video')['default']
|
||||||
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
|
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
|
||||||
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
|
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
|
||||||
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
|
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
|
||||||
|
@ -126,25 +137,40 @@ declare module '@vue/runtime-core' {
|
||||||
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
|
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
|
||||||
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
||||||
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
||||||
|
NAlert: typeof import('naive-ui')['NAlert']
|
||||||
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
||||||
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
NCode: typeof import('naive-ui')['NCode']
|
NCode: typeof import('naive-ui')['NCode']
|
||||||
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
||||||
|
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
|
NDatePicker: typeof import('naive-ui')['NDatePicker']
|
||||||
NDivider: typeof import('naive-ui')['NDivider']
|
NDivider: typeof import('naive-ui')['NDivider']
|
||||||
|
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
|
||||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||||
|
NForm: typeof import('naive-ui')['NForm']
|
||||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||||
NGi: typeof import('naive-ui')['NGi']
|
NGi: typeof import('naive-ui')['NGi']
|
||||||
NGrid: typeof import('naive-ui')['NGrid']
|
NGrid: typeof import('naive-ui')['NGrid']
|
||||||
NH1: typeof import('naive-ui')['NH1']
|
NH1: typeof import('naive-ui')['NH1']
|
||||||
|
NH2: typeof import('naive-ui')['NH2']
|
||||||
NH3: typeof import('naive-ui')['NH3']
|
NH3: typeof import('naive-ui')['NH3']
|
||||||
NIcon: typeof import('naive-ui')['NIcon']
|
NIcon: typeof import('naive-ui')['NIcon']
|
||||||
|
NImage: typeof import('naive-ui')['NImage']
|
||||||
|
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||||
|
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
|
||||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||||
NLabel: typeof import('naive-ui')['NLabel']
|
|
||||||
NLayout: typeof import('naive-ui')['NLayout']
|
NLayout: typeof import('naive-ui')['NLayout']
|
||||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||||
NMenu: typeof import('naive-ui')['NMenu']
|
NMenu: typeof import('naive-ui')['NMenu']
|
||||||
|
NProgress: typeof import('naive-ui')['NProgress']
|
||||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||||
|
NSlider: typeof import('naive-ui')['NSlider']
|
||||||
NSpin: typeof import('naive-ui')['NSpin']
|
NSpin: typeof import('naive-ui')['NSpin']
|
||||||
|
NStatistic: typeof import('naive-ui')['NStatistic']
|
||||||
|
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||||
|
NTable: typeof import('naive-ui')['NTable']
|
||||||
|
NTag: typeof import('naive-ui')['NTag']
|
||||||
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
||||||
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
||||||
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
|
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
|
||||||
|
@ -159,6 +185,8 @@ declare module '@vue/runtime-core' {
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
|
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
|
||||||
|
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
|
||||||
|
SipAuth: typeof import('./src/tools/sip-auth/sip-auth.vue')['default']
|
||||||
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
|
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
|
||||||
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
|
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
|
||||||
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
|
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']
|
||||||
|
|
|
@ -391,3 +391,11 @@ tools:
|
||||||
text-to-binary:
|
text-to-binary:
|
||||||
title: Text to ASCII binary
|
title: Text to ASCII binary
|
||||||
description: Convert text to its ASCII binary representation and vice versa.
|
description: Convert text to its ASCII binary representation and vice versa.
|
||||||
|
sip-auth:
|
||||||
|
password: SIP password
|
||||||
|
password-tips: Enter a password...
|
||||||
|
message: SIP message
|
||||||
|
message-tips: Paste your SIP message here...
|
||||||
|
result: Result
|
||||||
|
sucess: Sucess
|
||||||
|
failure: Failure
|
||||||
|
|
|
@ -387,3 +387,13 @@ tools:
|
||||||
text-to-binary:
|
text-to-binary:
|
||||||
title: 文本到 ASCII 二进制
|
title: 文本到 ASCII 二进制
|
||||||
description: 将文本转换为其 ASCII 二进制表示形式,反之亦然。
|
description: 将文本转换为其 ASCII 二进制表示形式,反之亦然。
|
||||||
|
sip-auth:
|
||||||
|
title: SIP 认证校验
|
||||||
|
description: 这个工具能够帮你验证 SIP 授权头。该工具所有运算过程都在浏览器中进行,并不会通过网络传输敏感信息,所以请放心使用。
|
||||||
|
password: SIP 密码
|
||||||
|
password-tips: 输入密码...
|
||||||
|
message: SIP 消息文本
|
||||||
|
message-tips: 在此处粘贴您的 SIP 消息...
|
||||||
|
result: 检测结果
|
||||||
|
sucess: 成功
|
||||||
|
failure: 失败
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { tool as base64FileConverter } from './base64-file-converter';
|
import { tool as base64FileConverter } from './base64-file-converter';
|
||||||
import { tool as base64StringConverter } from './base64-string-converter';
|
import { tool as base64StringConverter } from './base64-string-converter';
|
||||||
import { tool as basicAuthGenerator } from './basic-auth-generator';
|
import { tool as basicAuthGenerator } from './basic-auth-generator';
|
||||||
|
import { tool as sipAuth } from './sip-auth';
|
||||||
|
|
||||||
import { tool as asciiTextDrawer } from './ascii-text-drawer';
|
import { tool as asciiTextDrawer } from './ascii-text-drawer';
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ import { tool as yamlViewer } from './yaml-viewer';
|
||||||
export const toolsByCategory: ToolCategory[] = [
|
export const toolsByCategory: ToolCategory[] = [
|
||||||
{
|
{
|
||||||
name: 'Crypto',
|
name: 'Crypto',
|
||||||
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker],
|
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker, sipAuth],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Converter',
|
name: 'Converter',
|
||||||
|
|
12
src/tools/sip-auth/index.ts
Normal file
12
src/tools/sip-auth/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { ArrowsShuffle, ShieldCheck, ShieldLock } from '@vicons/tabler';
|
||||||
|
import { defineTool } from '../tool';
|
||||||
|
|
||||||
|
export const tool = defineTool({
|
||||||
|
name: 'SIP Authorization',
|
||||||
|
path: '/sip-auth',
|
||||||
|
description: 'This tool helps you to verify the SIP authorization header. The tool performs all calculations in the browser and does not transmit sensitive information over the network, so feel free to use it.',
|
||||||
|
keywords: ['sip', 'authorization'],
|
||||||
|
component: () => import('./sip-auth.vue'),
|
||||||
|
icon: ShieldCheck,
|
||||||
|
createdAt: new Date('2024-04-11'),
|
||||||
|
});
|
30
src/tools/sip-auth/sip-auth.service.test.ts
Normal file
30
src/tools/sip-auth/sip-auth.service.test.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { getAuthHeaderCheckResult } from './sip-auth.service';
|
||||||
|
|
||||||
|
const message = `REGISTER sip:172.21.169.226 SIP/2.0
|
||||||
|
Via: SIP/2.0/UDP 172.21.160.1:64712;rport;branch=z9hG4bKPja70ba2f5f4464081a3925196ebd4e7f9
|
||||||
|
Max-Forwards: 70
|
||||||
|
From: <sip:1000@172.21.169.226>;tag=8db088f1039e49b691e53e5f172ace98
|
||||||
|
To: <sip:1000@172.21.169.226>
|
||||||
|
Call-ID: be95702faee84a8ebc8ccf94c5ca5492
|
||||||
|
CSeq: 37808 REGISTER
|
||||||
|
User-Agent: MicroSIP/3.21.3
|
||||||
|
Contact: <sip:1000@172.21.160.1:64712;ob>
|
||||||
|
Expires: 300
|
||||||
|
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
|
||||||
|
Authorization: Digest username="1000", realm="172.21.169.226", nonce="d99a372c-340a-4ec9-91ed-53ad8f318ae0", uri="sip:172.21.169.226", response="7bdeb758d949a3c5e8d125fafbac1d5e", algorit=MD5, cnonce="3a8c445fb7ff48aeb8c422b00afaaddc", qop=auth, nc=00000001
|
||||||
|
Content-Length: 0`;
|
||||||
|
|
||||||
|
describe('getAuthHeaderCheckResult', () => {
|
||||||
|
it('should return "check success" for valid authentication', () => {
|
||||||
|
const password = '1234';
|
||||||
|
const result = getAuthHeaderCheckResult({ message, password });
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return "check failed" for invalid authentication', () => {
|
||||||
|
const password = '12345';
|
||||||
|
const result = getAuthHeaderCheckResult({ message, password });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
36
src/tools/sip-auth/sip-auth.service.ts
Normal file
36
src/tools/sip-auth/sip-auth.service.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { MD5 } from 'crypto-js';
|
||||||
|
|
||||||
|
export { getAuthHeaderCheckResult };
|
||||||
|
|
||||||
|
function getAuthHeaderCheckResult({ message, password }: { message: string; password: string }) {
|
||||||
|
let calculatedHash;
|
||||||
|
const method = extractComponent('^([A-Z]+) sip', message);
|
||||||
|
const username = extractComponent('username="([^"]+)"', message);
|
||||||
|
const realm = extractComponent('realm="([^"]+)"', message);
|
||||||
|
const uri = extractComponent('uri="([^"]+)"', message);
|
||||||
|
const nonce = extractComponent('[^c]nonce="([^"]+)"', message);
|
||||||
|
const cnonce = extractComponent('cnonce="([^"]+)"', message);
|
||||||
|
const nc = extractComponent('nc=([0-9a-f]+)', message);
|
||||||
|
const qop = extractComponent('qop="?(auth|auth-int)"?', message);
|
||||||
|
const response = extractComponent('response="([^"]+)"', message);
|
||||||
|
const ha1 = MD5(`${username}:${realm}:${password}`).toString();
|
||||||
|
const ha2 = MD5(`${method}:${uri}`).toString();
|
||||||
|
if (qop.toLowerCase() === 'auth') {
|
||||||
|
calculatedHash = MD5(`${ha1}:${nonce}:${nc}:${cnonce}:${qop}:${ha2}`).toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
calculatedHash = MD5(`${ha1}:${nonce}:${cnonce}`).toString();
|
||||||
|
}
|
||||||
|
return (response === calculatedHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractComponent(regex: string, source: string): string {
|
||||||
|
const processor = new RegExp(regex, 'i');
|
||||||
|
const matchResult = processor.exec(source);
|
||||||
|
if (matchResult !== null && matchResult.length > 1) {
|
||||||
|
return matchResult[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'Not Found';
|
||||||
|
}
|
||||||
|
}
|
39
src/tools/sip-auth/sip-auth.vue
Normal file
39
src/tools/sip-auth/sip-auth.vue
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getAuthHeaderCheckResult } from './sip-auth.service';
|
||||||
|
|
||||||
|
const password = ref('');
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
function transformer(value: string) {
|
||||||
|
if (value !== '' && password.value !== '') {
|
||||||
|
const Result = getAuthHeaderCheckResult({ message: value, password: password.value });
|
||||||
|
if (Result) {
|
||||||
|
return t('tools.sip-auth.sucess');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return t('tools.sip-auth.failure');
|
||||||
|
// return `11 calculatedHash: ${calculatedHash}, response: ${response}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div flex flex-col gap-4>
|
||||||
|
<c-input-text
|
||||||
|
v-model:value="password"
|
||||||
|
type="password"
|
||||||
|
:placeholder="$t('tools.sip-auth.password-tips')"
|
||||||
|
autofocus
|
||||||
|
raw-text
|
||||||
|
:label="$t('tools.sip-auth.password')"
|
||||||
|
/>
|
||||||
|
<format-transformer
|
||||||
|
:input-label="$t('tools.sip-auth.message')"
|
||||||
|
:input-placeholder="$t('tools.sip-auth.message-tips')"
|
||||||
|
:output-label="$t('tools.sip-auth.result')"
|
||||||
|
:transformer="transformer"
|
||||||
|
autosize
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
Loading…
Add table
Add a link
Reference in a new issue