Skip to content

Commit 362434c

Browse files
committed
feat: add SIP authentication
change icon
1 parent d3b32cc commit 362434c

File tree

8 files changed

+166
-2
lines changed

8 files changed

+166
-2
lines changed

components.d.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,28 @@ declare module '@vue/runtime-core' {
8989
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
9090
IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
9191
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
92+
'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
9293
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
94+
IconMdiArrowDown: typeof import('~icons/mdi/arrow-down')['default']
95+
IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default']
96+
IconMdiCamera: typeof import('~icons/mdi/camera')['default']
9397
IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
9498
IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
9599
IconMdiClose: typeof import('~icons/mdi/close')['default']
96100
IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
101+
IconMdiDeleteOutline: typeof import('~icons/mdi/delete-outline')['default']
102+
IconMdiDownload: typeof import('~icons/mdi/download')['default']
97103
IconMdiEye: typeof import('~icons/mdi/eye')['default']
98104
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
99105
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
106+
IconMdiPause: typeof import('~icons/mdi/pause')['default']
107+
IconMdiPlay: typeof import('~icons/mdi/play')['default']
108+
IconMdiRecord: typeof import('~icons/mdi/record')['default']
109+
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
100110
IconMdiSearch: typeof import('~icons/mdi/search')['default']
101111
IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
102112
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
113+
IconMdiVideo: typeof import('~icons/mdi/video')['default']
103114
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
104115
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
105116
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
@@ -126,25 +137,40 @@ declare module '@vue/runtime-core' {
126137
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
127138
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
128139
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
140+
NAlert: typeof import('naive-ui')['NAlert']
129141
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
142+
NCheckbox: typeof import('naive-ui')['NCheckbox']
130143
NCode: typeof import('naive-ui')['NCode']
131144
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
145+
NColorPicker: typeof import('naive-ui')['NColorPicker']
132146
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
147+
NDatePicker: typeof import('naive-ui')['NDatePicker']
133148
NDivider: typeof import('naive-ui')['NDivider']
149+
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
134150
NEllipsis: typeof import('naive-ui')['NEllipsis']
151+
NForm: typeof import('naive-ui')['NForm']
135152
NFormItem: typeof import('naive-ui')['NFormItem']
136153
NGi: typeof import('naive-ui')['NGi']
137154
NGrid: typeof import('naive-ui')['NGrid']
138155
NH1: typeof import('naive-ui')['NH1']
156+
NH2: typeof import('naive-ui')['NH2']
139157
NH3: typeof import('naive-ui')['NH3']
140158
NIcon: typeof import('naive-ui')['NIcon']
159+
NImage: typeof import('naive-ui')['NImage']
160+
NInputGroup: typeof import('naive-ui')['NInputGroup']
161+
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
141162
NInputNumber: typeof import('naive-ui')['NInputNumber']
142-
NLabel: typeof import('naive-ui')['NLabel']
143163
NLayout: typeof import('naive-ui')['NLayout']
144164
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
145165
NMenu: typeof import('naive-ui')['NMenu']
166+
NProgress: typeof import('naive-ui')['NProgress']
146167
NScrollbar: typeof import('naive-ui')['NScrollbar']
168+
NSlider: typeof import('naive-ui')['NSlider']
147169
NSpin: typeof import('naive-ui')['NSpin']
170+
NStatistic: typeof import('naive-ui')['NStatistic']
171+
NSwitch: typeof import('naive-ui')['NSwitch']
172+
NTable: typeof import('naive-ui')['NTable']
173+
NTag: typeof import('naive-ui')['NTag']
148174
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
149175
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
150176
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
@@ -159,6 +185,8 @@ declare module '@vue/runtime-core' {
159185
RouterLink: typeof import('vue-router')['RouterLink']
160186
RouterView: typeof import('vue-router')['RouterView']
161187
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
188+
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
189+
SipAuth: typeof import('./src/tools/sip-auth/sip-auth.vue')['default']
162190
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
163191
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
164192
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']

locales/en.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,11 @@ tools:
391391
text-to-binary:
392392
title: Text to ASCII binary
393393
description: Convert text to its ASCII binary representation and vice versa.
394+
sip-auth:
395+
password: SIP password
396+
password-tips: Enter a password...
397+
message: SIP message
398+
message-tips: Paste your SIP message here...
399+
result: Result
400+
sucess: Sucess
401+
failure: Failure

locales/zh.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,13 @@ tools:
387387
text-to-binary:
388388
title: 文本到 ASCII 二进制
389389
description: 将文本转换为其 ASCII 二进制表示形式,反之亦然。
390+
sip-auth:
391+
title: SIP 认证校验
392+
description: 这个工具能够帮你验证 SIP 授权头。该工具所有运算过程都在浏览器中进行,并不会通过网络传输敏感信息,所以请放心使用。
393+
password: SIP 密码
394+
password-tips: 输入密码...
395+
message: SIP 消息文本
396+
message-tips: 在此处粘贴您的 SIP 消息...
397+
result: 检测结果
398+
sucess: 成功
399+
failure: 失败

src/tools/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { tool as base64FileConverter } from './base64-file-converter';
22
import { tool as base64StringConverter } from './base64-string-converter';
33
import { tool as basicAuthGenerator } from './basic-auth-generator';
4+
import { tool as sipAuth } from './sip-auth';
45

56
import { tool as asciiTextDrawer } from './ascii-text-drawer';
67

@@ -85,7 +86,7 @@ import { tool as yamlViewer } from './yaml-viewer';
8586
export const toolsByCategory: ToolCategory[] = [
8687
{
8788
name: 'Crypto',
88-
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker],
89+
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker, sipAuth],
8990
},
9091
{
9192
name: 'Converter',

src/tools/sip-auth/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ArrowsShuffle, ShieldCheck, ShieldLock } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'SIP Authorization',
6+
path: '/sip-auth',
7+
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.',
8+
keywords: ['sip', 'authorization'],
9+
component: () => import('./sip-auth.vue'),
10+
icon: ShieldCheck,
11+
createdAt: new Date('2024-04-11'),
12+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { getAuthHeaderCheckResult } from './sip-auth.service';
3+
4+
const message = `REGISTER sip:172.21.169.226 SIP/2.0
5+
Via: SIP/2.0/UDP 172.21.160.1:64712;rport;branch=z9hG4bKPja70ba2f5f4464081a3925196ebd4e7f9
6+
Max-Forwards: 70
7+
From: <sip:[email protected]>;tag=8db088f1039e49b691e53e5f172ace98
8+
9+
Call-ID: be95702faee84a8ebc8ccf94c5ca5492
10+
CSeq: 37808 REGISTER
11+
User-Agent: MicroSIP/3.21.3
12+
Contact: <sip:[email protected]:64712;ob>
13+
Expires: 300
14+
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
15+
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
16+
Content-Length: 0`;
17+
18+
describe('getAuthHeaderCheckResult', () => {
19+
it('should return "check success" for valid authentication', () => {
20+
const password = '1234';
21+
const result = getAuthHeaderCheckResult({ message, password });
22+
expect(result).toBe(true);
23+
});
24+
25+
it('should return "check failed" for invalid authentication', () => {
26+
const password = '12345';
27+
const result = getAuthHeaderCheckResult({ message, password });
28+
expect(result).toBe(false);
29+
});
30+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { MD5 } from 'crypto-js';
2+
3+
export { getAuthHeaderCheckResult };
4+
5+
function getAuthHeaderCheckResult({ message, password }: { message: string; password: string }) {
6+
let calculatedHash;
7+
const method = extractComponent('^([A-Z]+) sip', message);
8+
const username = extractComponent('username="([^"]+)"', message);
9+
const realm = extractComponent('realm="([^"]+)"', message);
10+
const uri = extractComponent('uri="([^"]+)"', message);
11+
const nonce = extractComponent('[^c]nonce="([^"]+)"', message);
12+
const cnonce = extractComponent('cnonce="([^"]+)"', message);
13+
const nc = extractComponent('nc=([0-9a-f]+)', message);
14+
const qop = extractComponent('qop="?(auth|auth-int)"?', message);
15+
const response = extractComponent('response="([^"]+)"', message);
16+
const ha1 = MD5(`${username}:${realm}:${password}`).toString();
17+
const ha2 = MD5(`${method}:${uri}`).toString();
18+
if (qop.toLowerCase() === 'auth') {
19+
calculatedHash = MD5(`${ha1}:${nonce}:${nc}:${cnonce}:${qop}:${ha2}`).toString();
20+
}
21+
else {
22+
calculatedHash = MD5(`${ha1}:${nonce}:${cnonce}`).toString();
23+
}
24+
return (response === calculatedHash);
25+
}
26+
27+
function extractComponent(regex: string, source: string): string {
28+
const processor = new RegExp(regex, 'i');
29+
const matchResult = processor.exec(source);
30+
if (matchResult !== null && matchResult.length > 1) {
31+
return matchResult[1];
32+
}
33+
else {
34+
return 'Not Found';
35+
}
36+
}

src/tools/sip-auth/sip-auth.vue

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script setup lang="ts">
2+
import { getAuthHeaderCheckResult } from './sip-auth.service';
3+
4+
const password = ref('');
5+
const { t } = useI18n();
6+
7+
function transformer(value: string) {
8+
if (value !== '' && password.value !== '') {
9+
const Result = getAuthHeaderCheckResult({ message: value, password: password.value });
10+
if (Result) {
11+
return t('tools.sip-auth.sucess');
12+
}
13+
else {
14+
return t('tools.sip-auth.failure');
15+
// return `11 calculatedHash: ${calculatedHash}, response: ${response}`;
16+
}
17+
}
18+
}
19+
</script>
20+
21+
<template>
22+
<div flex flex-col gap-4>
23+
<c-input-text
24+
v-model:value="password"
25+
type="password"
26+
:placeholder="$t('tools.sip-auth.password-tips')"
27+
autofocus
28+
raw-text
29+
:label="$t('tools.sip-auth.password')"
30+
/>
31+
<format-transformer
32+
:input-label="$t('tools.sip-auth.message')"
33+
:input-placeholder="$t('tools.sip-auth.message-tips')"
34+
:output-label="$t('tools.sip-auth.result')"
35+
:transformer="transformer"
36+
autosize
37+
/>
38+
</div>
39+
</template>

0 commit comments

Comments
 (0)