nskd-lbr 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,156 @@
1
+ NSD LICENSE
2
+ Version 1.0, May 2025
3
+ (NON STANDARD DISTRIBUTION LICENSE)
4
+
5
+ ================================================================================
6
+
7
+ Copyright (C) 2025 Douxx.tech <douxx@douxx.tech>
8
+
9
+ Permission is hereby granted to copy and distribute verbatim copies of this
10
+ license document, but modification is not permitted without explicit written
11
+ consent from the copyright holder.
12
+
13
+ ================================================================================
14
+
15
+ PREAMBLE
16
+
17
+ The NSD License (Non Standard Distribution License) is designed to provide
18
+ a balance between open source principles and commercial viability. This
19
+ license ensures that software remains accessible while protecting the
20
+ rights and interests of original authors and contributors.
21
+
22
+ This license grants users the freedom to use, study, and distribute the
23
+ software while maintaining proper attribution and ensuring that derivative
24
+ works remain under compatible terms.
25
+
26
+ The goal is to foster innovation and collaboration while respecting
27
+ intellectual property rights and encouraging responsible development
28
+ practices.
29
+
30
+ ================================================================================
31
+
32
+ TERMS AND CONDITIONS
33
+
34
+ 1. DEFINITIONS
35
+
36
+ a) "License" refers to this NSD License version 1.0.
37
+
38
+ b) "Work" refers to the copyrightable software, documentation, or other
39
+ materials licensed under this License.
40
+
41
+ c) "You" refers to the individual or legal entity exercising rights
42
+ under this License.
43
+
44
+ d) "Derivative Work" means a work based upon the Work, such as a
45
+ revision, modification, translation, abridgment, condensation, or
46
+ any other form in which the Work may be recast, transformed, or
47
+ adapted.
48
+
49
+ e) "Distribution" means making copies of the Work available to third
50
+ parties through any medium.
51
+
52
+ 2. GRANT OF RIGHTS
53
+
54
+ Subject to the terms and conditions of this License, You are hereby
55
+ granted a worldwide, royalty-free, non-exclusive license to:
56
+
57
+ a) Use the Work for any purpose, including commercial purposes
58
+ b) Study and examine the Work
59
+ c) Make copies of the Work
60
+ d) Distribute copies of the Work
61
+ e) Create and distribute Derivative Works
62
+
63
+ 3. CONDITIONS FOR USE AND DISTRIBUTION
64
+
65
+ a) ATTRIBUTION: You must retain all copyright notices, this License,
66
+ and any other notices that were included with the Work. When
67
+ distributing the Work or Derivative Works, you must provide clear
68
+ attribution to the original author(s).
69
+
70
+ b) LICENSE INCLUSION: Any distribution of the Work must include a copy
71
+ of this License or a clear reference to where it can be obtained.
72
+
73
+ c) SOURCE CODE AVAILABILITY: If you distribute the Work in binary or
74
+ compiled form, you must either:
75
+ - Include the complete source code with the distribution, or
76
+ - Provide a written offer to supply the source code upon request
77
+ for a period of at least three years
78
+
79
+ d) DERIVATIVE WORKS: Derivative Works must be clearly marked as such
80
+ and must include a prominent notice describing the modifications
81
+ made and the date of modification.
82
+
83
+ 4. COPYLEFT PROVISION
84
+
85
+ Any Derivative Work you create must be licensed under terms that are
86
+ compatible with this License. You may add additional permissions but
87
+ may not impose additional restrictions that contradict the terms of
88
+ this License.
89
+
90
+ 5. COMMERCIAL USE
91
+
92
+ Commercial use is explicitly permitted under this License, provided
93
+ all other terms and conditions are met. You may charge fees for
94
+ distribution, support, or warranty services.
95
+
96
+ 6. NO WARRANTY
97
+
98
+ THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
99
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
100
+ FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT
101
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES,
102
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
103
+ ARISING FROM, OUT OF, OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER
104
+ DEALINGS IN THE WORK.
105
+
106
+ 7. TERMINATION
107
+
108
+ This License and the rights granted hereunder will terminate automatically
109
+ if You fail to comply with the terms herein. However, parties who have
110
+ received copies or rights from You under this License will not have their
111
+ licenses terminated provided they remain in full compliance.
112
+
113
+ 8. MISCELLANEOUS
114
+
115
+ a) If any provision of this License is held to be unenforceable, such
116
+ provision shall be reformed only to the extent necessary to make it
117
+ enforceable.
118
+
119
+ b) This License represents the complete agreement concerning the subject
120
+ matter hereof.
121
+
122
+ c) This License shall be governed by the laws of Switzerland's Juridiction,
123
+ excluding conflict of law provisions.
124
+
125
+ 9. LICENSE VERSIONING
126
+
127
+ The copyright holder may publish revised versions of the NSD License.
128
+ Each version will be given a distinguishing version number. Unless
129
+ otherwise specified, you may use the Work under the terms of the version
130
+ of the License under which you originally received it.
131
+
132
+ 10. APPLICATION OF LICENSE
133
+
134
+ To properly apply this License to any work, the copyright holder must
135
+ include a copyright notice and license identification statement in each
136
+ source file or in a prominent location within the work's documentation.
137
+
138
+ The required notice shall contain at minimum:
139
+ a) A copyright statement identifying the copyright holder and year
140
+ b) A clear statement that the work is licensed under this License
141
+ c) Reference to where the complete License text may be obtained
142
+
143
+ The standard format for such notice is:
144
+
145
+ Copyright (C) [YEAR] [COPYRIGHT HOLDER NAME] <[CONTACT EMAIL]>
146
+
147
+ This work is licensed under the NSD License v1.0.
148
+ You may obtain a copy of the License at: https://douxx.tech/NSD
149
+
150
+ Failure to include proper license notices may affect the enforceability
151
+ of the rights granted herein and may result in copyright infringement
152
+ claims.
153
+
154
+ ================================================================================
155
+
156
+ END OF TERMS AND CONDITIONS
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # NoSkid Certificate Library
2
+
3
+ A JavaScript library for working with NoSkid certificates.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [API Reference](#api-reference)
10
+ - [Examples](#examples)
11
+ - [Configuration](#configuration)
12
+ - [Error Handling](#error-handling)
13
+ - [Contributing](#contributing)
14
+ - [License](#license)
15
+
16
+ ## Installation
17
+
18
+ ### Browser
19
+
20
+ ```html
21
+ <script src="https://lbr.noskid.today"></script>
22
+ ```
23
+
24
+ ### Node.js
25
+
26
+ ```bash
27
+ npm install nskd-lbr
28
+ ```
29
+
30
+ ```js
31
+ const NskdLbr = require('nskd-lbr');
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### Basic Usage
37
+
38
+ ```js
39
+ // Initialize the library
40
+ const nskd = new NskdLbr();
41
+
42
+ // Verify a certificate from a file
43
+ const fileInput = document.getElementById('certificate-file');
44
+ fileInput.addEventListener('change', async (event) => {
45
+ try {
46
+ const result = await nskd.loadFromFile(event.target.files[0]);
47
+ if (result.valid) {
48
+ console.log('Certificate is valid!');
49
+ console.log(nskd.getFormattedDetails());
50
+ } else {
51
+ console.log('Certificate is invalid:', result.message);
52
+ }
53
+ } catch (error) {
54
+ console.error('Error:', error.message);
55
+ }
56
+ });
57
+
58
+ // Verify a certificate with a key
59
+ const verificationKey = 'a1b2c3d4e5f6...'; // 64-character hex string
60
+ nskd.verifyWithKey(verificationKey)
61
+ .then(result => {
62
+ if (result.valid) {
63
+ console.log('Certificate is valid!');
64
+ } else {
65
+ console.log('Certificate is invalid:', result.message);
66
+ }
67
+ })
68
+ .catch(error => {
69
+ console.error('Error:', error.message);
70
+ });
71
+ ```
72
+
73
+ ## API Reference
74
+
75
+ ### Constructor
76
+
77
+ ```javascript
78
+ new NskdLbr(options)
79
+ ```
80
+
81
+ **Options:**
82
+
83
+ | Option | Type | Default | Description |
84
+ | - | - | - | - |
85
+ | `apiUrl` | string | `'https://check.noskid.today/'` | The API endpoint for certificate verification |
86
+ | `debug` | boolean | `false` | Enable debug logging |
87
+ | `timeout` | number | `10000` | Request timeout in milliseconds |
88
+ | `strictCheck` | boolean | `true` | Validate local data against API response |
89
+ | `onnskdLbrLog` | function | `null` | Custom logging function |
90
+
91
+ ### Methods
92
+
93
+ | Method | Description | Returns |
94
+ | - | - | - |
95
+ | `loadFromFile(file)` | Load and verify a certificate from a PNG file | `Promise<Object>` Verification result |
96
+ | `verifyWithKey(key)` | Verify a certificate using a verification key | `Promise<Object>` Verification result |
97
+ | `getCertificateData()` | Get the current certificate data | `Object|null` Certificate data or null |
98
+ | `isValidCertificate()` | Check if the certificate is valid | `boolean` |
99
+ | `getFormattedDetails()` | Get formatted certificate details | `string` |
100
+ | `reset()` | Reset the certificate data | `void` |
101
+
102
+ ### Logging
103
+
104
+ The library provides a logging method that can be used to track the verification process:
105
+
106
+ ```js
107
+ nskd.nskdLbrLog(message, level);
108
+ ```
109
+
110
+ **Levels:**
111
+
112
+ - `info` (default)
113
+ - `error`
114
+ - `warning`
115
+ - `success`
116
+
117
+ ## Examples
118
+
119
+ ### Custom Logging
120
+
121
+ ```js
122
+ const nskd = new NskdLbr({
123
+ onnskdLbrLog: (message, level) => {
124
+ // Custom logging implementation
125
+ console.log(`[Custom Log] [${level.toUpperCase()}] ${message}`);
126
+ }
127
+ });
128
+ ```
129
+
130
+ ### Disabling Strict Check
131
+
132
+ ```js
133
+ const nskd = new NskdLbr({
134
+ strictCheck: false
135
+ });
136
+ ```
137
+
138
+ ## Configuration
139
+
140
+ The library can be configured with various options to suit your needs. The most important options are:
141
+
142
+ - `apiUrl`: The endpoint for the NoSkid verification API.
143
+ - `debug`: Enable or disable debug logging.
144
+ - `timeout`: Set the request timeout in milliseconds.
145
+ - `strictCheck`: Enable or disable strict validation of local data against API response.
146
+
147
+ ## Error Handling
148
+
149
+ The library throws errors in case of issues during the verification process. It is recommended to wrap the library calls in try-catch blocks to handle these errors gracefully.
150
+
151
+ ```javascript
152
+ try {
153
+ const result = await nskd.loadFromFile(file);
154
+ // Handle the result
155
+ } catch (error) {
156
+ console.error('Verification failed:', error.message);
157
+ }
158
+ ```
159
+
160
+ ## Contributing
161
+
162
+ Contributions are welcome! Please open an issue or submit a pull request on the GitHub repository.
163
+
164
+ ## License
165
+
166
+ This library is licensed under the NSDv1.0 License. See the LICENSE file for more information.
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "nskd-lbr",
3
+ "version": "1.0.0",
4
+ "description": "A JavaScript library for working with NoSkid certificates.",
5
+ "main": "src/nskd-lbr.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/douxxtech/noskid.today.git"
12
+ },
13
+ "keywords": [
14
+ "noskid",
15
+ "certificate"
16
+ ],
17
+ "author": "Douxx",
18
+ "license": "SEE LICENSE IN LICENSE",
19
+ "bugs": {
20
+ "url": "https://github.com/douxxtech/noskid.today/issues"
21
+ },
22
+ "homepage": "https://github.com/douxxtech/noskid.today#readme"
23
+ }
@@ -0,0 +1,414 @@
1
+ /**
2
+ * NoSkid Certificate Library
3
+ * A JavaScript library for working with NoSkid certificates
4
+ *
5
+ * @version 1.0.0
6
+ * @author Douxx <douxx@douxx.tech>
7
+ * @param {string} [options.apiUrl='https://check.noskid.today/'] - Logs debug messages to console
8
+ * @param {boolean} [options.debug=false] - Logs debug messages to console
9
+ * @param {boolean} [options.strictCheck=true] - Whether to validate local data against API response
10
+ * @param {integer} [options.timeout=10000] - API request timeout in milliseconds
11
+ * @param {function} [options.onLog=null] - Whether to validate local data against API response
12
+ */
13
+
14
+ class NskdLbr {
15
+ constructor(options = {}) {
16
+ this.apiUrl = options.apiUrl || 'https://check.noskid.today/';
17
+ this.debug = options.debug || false;
18
+ this.timeout = options.timeout || 10000;
19
+ this.strictCheck = options.strictCheck !== undefined ? options.strictCheck : true;
20
+ this.onLog = options.onLog || null;
21
+ this.certificateData = null;
22
+ this.verificationKey = null;
23
+ this.localData = null;
24
+ this.isValid = false;
25
+ }
26
+
27
+ /**
28
+ * Logs messages with different levels
29
+ * @param {string} message - The message to Log
30
+ */
31
+ nskdLbrLog(message, level = 'info') {
32
+ if (this.debug) {
33
+ const timestamp = new Date().toLocaleTimeString();
34
+ const prefix = `[${timestamp}] NoSkid:`;
35
+
36
+ switch (level) {
37
+ case 'error':
38
+ console.error(prefix, message);
39
+ break;
40
+ case 'warning':
41
+ console.warn(prefix, message);
42
+ break;
43
+ case 'success':
44
+ console.nskdLbrLog(`%c${prefix} ${message}`, 'color: green');
45
+ break;
46
+ default:
47
+ console.nskdLbrLog(prefix, message);
48
+ }
49
+ }
50
+
51
+ if (this.onLog && typeof this.onLog === 'function') {
52
+ this.onLog(message, level);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Load and verify a certificate from a PNG file
58
+ * @param {File} file - The PNG certificate file
59
+ * @returns {Promise<Object>} Verification result
60
+ */
61
+ async loadFromFile(file) {
62
+ try {
63
+ this.nskdLbrLog('Starting certificate verification process...', 'info');
64
+
65
+ if (!file) {
66
+ throw new Error('No file provided');
67
+ }
68
+
69
+ if (!file.name.toLowerCase().endsWith('.png')) {
70
+ throw new Error('File must be a PNG image');
71
+ }
72
+
73
+ this.nskdLbrLog(`Processing certificate file: ${file.name}`, 'info');
74
+
75
+ const arrayBuffer = await this.readFileAsArrayBuffer(file);
76
+
77
+ const extractedText = await this.extractTextFromPng(arrayBuffer);
78
+ if (!extractedText) {
79
+ throw new Error('Could not extract verification data from file');
80
+ }
81
+
82
+ this.verificationKey = this.extractVerificationKey(extractedText);
83
+ if (!this.verificationKey) {
84
+ throw new Error('No valid verification key found in certificate');
85
+ }
86
+
87
+ this.nskdLbrLog('Successfully extracted verification key', 'success');
88
+
89
+ this.localData = this.extractLocalData(extractedText);
90
+ if (this.localData) {
91
+ this.nskdLbrLog('Local certificate data extracted:', 'info');
92
+ this.nskdLbrLog(`Username: ${this.localData.username}`, 'info');
93
+ this.nskdLbrLog(`Creation Date: ${this.localData.creationDate}`, 'info');
94
+ }
95
+
96
+ const result = await this.verifyWithAPI();
97
+ return result;
98
+
99
+ } catch (error) {
100
+ this.nskdLbrLog(`Error loading certificate: ${error.message}`, 'error');
101
+ throw error;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Verify a certificate using a verification key directly
107
+ * @param {string} key - The verification key (64-character hex string)
108
+ * @returns {Promise<Object>} Verification result
109
+ */
110
+ async verifyWithKey(key) {
111
+ try {
112
+ if (!key || typeof key !== 'string') {
113
+ throw new Error('Invalid verification key provided');
114
+ }
115
+
116
+ if (!/^[a-f0-9]{64}$/i.test(key)) {
117
+ throw new Error('Verification key must be a 64-character hexadecimal string');
118
+ }
119
+
120
+ this.verificationKey = key.toLowerCase();
121
+ this.nskdLbrLog(`Verifying certificate with key: ${this.verificationKey.substring(0, 16)}...`, 'info');
122
+
123
+ const result = await this.verifyWithAPI();
124
+ return result;
125
+
126
+ } catch (error) {
127
+ this.nskdLbrLog(`Error verifying certificate: ${error.message}`, 'error');
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Get the current certificate data
134
+ * @returns {Object|null} Certificate data or null if not loaded/verified
135
+ */
136
+ getCertificateData() {
137
+ return this.certificateData;
138
+ }
139
+
140
+ /**
141
+ * Check if the certificate is valid
142
+ * @returns {boolean} True if certificate is valid
143
+ */
144
+ isValidCertificate() {
145
+ return this.isValid;
146
+ }
147
+
148
+ /**
149
+ * Get formatted certificate details as a string
150
+ * @returns {string} Formatted certificate details
151
+ */
152
+ getFormattedDetails() {
153
+ if (!this.certificateData) {
154
+ return 'No certificate data available';
155
+ }
156
+
157
+ const data = this.certificateData;
158
+ return `
159
+ Certificate Details:
160
+ - Certificate #: ${data.certificate_number}
161
+ - Username: ${data.username}
162
+ - Percentage: ${data.percentage}%
163
+ - Creation Date: ${data.creationDate}
164
+ - Country: ${data.country} (${data.countryCode})
165
+ `.trim();
166
+ }
167
+
168
+ /**
169
+ * Reset the certificate data
170
+ */
171
+ reset() {
172
+ this.certificateData = null;
173
+ this.verificationKey = null;
174
+ this.localData = null;
175
+ this.isValid = false;
176
+ this.nskdLbrLog('Certificate data reset', 'info');
177
+ }
178
+
179
+ // Private methods
180
+
181
+ /**
182
+ * Read file as array buffer
183
+ * @private
184
+ */
185
+ readFileAsArrayBuffer(file) {
186
+ return new Promise((resolve, reject) => {
187
+ const reader = new FileReader();
188
+ reader.onload = (e) => resolve(e.target.result);
189
+ reader.onerror = () => reject(new Error('Error reading file'));
190
+ reader.readAsArrayBuffer(file);
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Extract text from PNG tEXt chunk
196
+ * @private
197
+ */
198
+ async extractTextFromPng(arrayBuffer) {
199
+ try {
200
+ const bytes = new Uint8Array(arrayBuffer);
201
+
202
+ // Check PNG header
203
+ if (!(bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47)) {
204
+ throw new Error("Not a valid PNG file");
205
+ }
206
+
207
+ let pos = 8; // Skip PNG header
208
+ let extractedText = null;
209
+
210
+ while (pos < bytes.length - 12) {
211
+ const length = (
212
+ (bytes[pos] << 24) |
213
+ (bytes[pos + 1] << 16) |
214
+ (bytes[pos + 2] << 8) |
215
+ (bytes[pos + 3])
216
+ );
217
+
218
+ const type = String.fromCharCode(
219
+ bytes[pos + 4],
220
+ bytes[pos + 5],
221
+ bytes[pos + 6],
222
+ bytes[pos + 7]
223
+ );
224
+
225
+ if (type === 'tEXt') {
226
+ const chunkData = bytes.slice(pos + 8, pos + 8 + length);
227
+ const text = new TextDecoder('utf-8').decode(chunkData);
228
+
229
+ const separatorIndex = text.indexOf('\0');
230
+ if (separatorIndex !== -1) {
231
+ const keyword = text.substring(0, separatorIndex);
232
+ const value = text.substring(separatorIndex + 1);
233
+
234
+ if (keyword === 'noskid-key') {
235
+ extractedText = value;
236
+ break;
237
+ }
238
+ }
239
+ }
240
+
241
+ pos += 8 + length + 4;
242
+ }
243
+
244
+ if (extractedText) {
245
+ this.nskdLbrLog("Certificate data extracted successfully from PNG", 'success');
246
+ return extractedText;
247
+ } else {
248
+ this.nskdLbrLog("No 'noskid-key' text chunk found in PNG", 'error');
249
+ return null;
250
+ }
251
+ } catch (error) {
252
+ this.nskdLbrLog(`Error extracting text from PNG: ${error.message}`, 'error');
253
+ return null;
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Extract verification key from certificate text
259
+ * @private
260
+ */
261
+ extractVerificationKey(text) {
262
+ try {
263
+ const keyPattern = /-*BEGIN NOSKID KEY-*\s*([a-f0-9]{64})/i;
264
+ const match = text.match(keyPattern);
265
+ return match ? match[1].toLowerCase() : null;
266
+ } catch (error) {
267
+ this.nskdLbrLog(`Error extracting verification key: ${error.message}`, 'error');
268
+ return null;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Extract local certificate data
274
+ * @private
275
+ */
276
+ extractLocalData(text) {
277
+ try {
278
+ const keyPattern = /-----BEGIN NOSKID KEY-----\s*([a-f0-9]+)\s*([A-Za-z0-9+/=]+)\s*([A-Za-z0-9+/=]+)\s*-----END NOSKID KEY-----/;
279
+ const match = text.match(keyPattern);
280
+
281
+ if (!match) return null;
282
+
283
+ const certInfoEncoded = match[2];
284
+ const certInfoDecoded = atob(certInfoEncoded.replace(/=/g, ''));
285
+ const usernameMatch = certInfoDecoded.match(/CERT-\d+-(.+)/);
286
+ const username = usernameMatch ? usernameMatch[1] : null;
287
+
288
+ const dateInfoEncoded = match[3];
289
+ const dateInfoDecoded = atob(dateInfoEncoded.replace(/=/g, ''));
290
+ const dateMatch = dateInfoDecoded.match(/CREATED-(.+)/);
291
+ const creationDate = dateMatch ? dateMatch[1] : null;
292
+
293
+ return { username, creationDate };
294
+ } catch (error) {
295
+ this.nskdLbrLog(`Error extracting local data: ${error.message}`, 'error');
296
+ return null;
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Verify certificate with API
302
+ * @private
303
+ */
304
+ async verifyWithAPI() {
305
+ try {
306
+ this.nskdLbrLog('Verifying certificate with server...', 'info');
307
+
308
+ const controller = new AbortController();
309
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
310
+
311
+ const response = await fetch(
312
+ `${this.apiUrl}?key=${encodeURIComponent(this.verificationKey)}`,
313
+ {
314
+ signal: controller.signal,
315
+ headers: {
316
+ 'User-Agent': 'NskdLbr/1.0.0'
317
+ }
318
+ }
319
+ );
320
+
321
+ clearTimeout(timeoutId);
322
+
323
+ if (!response.ok) {
324
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
325
+ }
326
+
327
+ const apiData = await response.json();
328
+
329
+ if (!apiData.success) {
330
+ this.isValid = false;
331
+ this.nskdLbrLog(`Certificate verification failed: ${apiData.message}`, 'error');
332
+ return {
333
+ valid: false,
334
+ message: apiData.message,
335
+ cached: apiData.cached || false
336
+ };
337
+ }
338
+
339
+ // Compare local data with API data if available and strictCheck is enabled
340
+ if (this.localData && this.strictCheck) {
341
+ const validationResult = this.compareData(this.localData, apiData.data);
342
+ if (!validationResult.valid) {
343
+ this.isValid = false;
344
+ this.nskdLbrLog('Certificate data mismatch!', 'error');
345
+ this.nskdLbrLog(`Mismatch reason: ${validationResult.reason}`, 'error');
346
+ this.nskdLbrLog('Note: Strict checking is enabled. Set strictCheck to false to skip local data validation.', 'warning');
347
+ return {
348
+ valid: false,
349
+ message: `Data mismatch: ${validationResult.reason}`,
350
+ cached: apiData.cached || false,
351
+ strictCheck: true
352
+ };
353
+ }
354
+ this.nskdLbrLog('Local data validation passed', 'success');
355
+ } else if (this.localData && !this.strictCheck) {
356
+ this.nskdLbrLog('Strict checking disabled - skipping local data validation', 'warning');
357
+ }
358
+
359
+ // Certificate is valid
360
+ this.isValid = true;
361
+ this.certificateData = apiData.data;
362
+ this.nskdLbrLog('Certificate is VALID!', 'success');
363
+
364
+ return {
365
+ valid: true,
366
+ message: 'Certificate verified successfully',
367
+ data: apiData.data,
368
+ cached: apiData.cached || false,
369
+ strictCheck: this.strictCheck
370
+ };
371
+
372
+ } catch (error) {
373
+ if (error.name === 'AbortError') {
374
+ throw new Error('Request timeout - server took too long to respond');
375
+ }
376
+ throw new Error(`API verification failed: ${error.message}`);
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Compare local and API data
382
+ * @private
383
+ */
384
+ compareData(localData, apiData) {
385
+ if (!localData || !apiData) {
386
+ return { valid: false, reason: 'Missing data for comparison' };
387
+ }
388
+
389
+ if (localData.username !== apiData.username) {
390
+ return {
391
+ valid: false,
392
+ reason: `Username mismatch: Local=${localData.username}, API=${apiData.username}`
393
+ };
394
+ }
395
+
396
+ const localDateMinutes = localData.creationDate.substring(0, 16);
397
+ const apiDateMinutes = apiData.creationDate.substring(0, 16);
398
+
399
+ if (localDateMinutes !== apiDateMinutes) {
400
+ return {
401
+ valid: false,
402
+ reason: `Creation date mismatch: Local=${localDateMinutes}, API=${apiDateMinutes}`
403
+ };
404
+ }
405
+
406
+ return { valid: true };
407
+ }
408
+ }
409
+
410
+ if (typeof module !== 'undefined' && module.exports) {
411
+ module.exports = NskdLbr;
412
+ } else {
413
+ window.NskdLbr = NskdLbr;
414
+ }
@@ -0,0 +1,8 @@
1
+ class NskdLbr{constructor(t={}){this.apiUrl=t.apiUrl||"https://check.noskid.today/",this.debug=t.debug||!1,this.timeout=t.timeout||1e4,this.strictCheck=t.strictCheck!==void 0?t.strictCheck:!0,this.onLog=t.onLog||null,this.certificateData=null,this.verificationKey=null,this.localData=null,this.isValid=!1}nskdLbrLog(t,e="info"){if(this.debug){const i=`[${new Date().toLocaleTimeString()}] NoSkid:`;switch(e){case"error":console.error(i,t);break;case"warning":console.warn(i,t);break;case"success":console.nskdLbrLog(`%c${i} ${t}`,"color: green");break;default:console.nskdLbrLog(i,t)}}this.onLog&&typeof this.onLog=="function"&&this.onLog(t,e)}async loadFromFile(t){try{if(this.nskdLbrLog("Starting certificate verification process...","info"),!t)throw new Error("No file provided");if(!t.name.toLowerCase().endsWith(".png"))throw new Error("File must be a PNG image");this.nskdLbrLog(`Processing certificate file: ${t.name}`,"info");const e=await this.readFileAsArrayBuffer(t),r=await this.extractTextFromPng(e);if(!r)throw new Error("Could not extract verification data from file");if(this.verificationKey=this.extractVerificationKey(r),!this.verificationKey)throw new Error("No valid verification key found in certificate");return this.nskdLbrLog("Successfully extracted verification key","success"),this.localData=this.extractLocalData(r),this.localData&&(this.nskdLbrLog("Local certificate data extracted:","info"),this.nskdLbrLog(`Username: ${this.localData.username}`,"info"),this.nskdLbrLog(`Creation Date: ${this.localData.creationDate}`,"info")),await this.verifyWithAPI()}catch(e){throw this.nskdLbrLog(`Error loading certificate: ${e.message}`,"error"),e}}async verifyWithKey(t){try{if(!t||typeof t!="string")throw new Error("Invalid verification key provided");if(!/^[a-f0-9]{64}$/i.test(t))throw new Error("Verification key must be a 64-character hexadecimal string");return this.verificationKey=t.toLowerCase(),this.nskdLbrLog(`Verifying certificate with key: ${this.verificationKey.substring(0,16)}...`,"info"),await this.verifyWithAPI()}catch(e){throw this.nskdLbrLog(`Error verifying certificate: ${e.message}`,"error"),e}}getCertificateData(){return this.certificateData}isValidCertificate(){return this.isValid}getFormattedDetails(){if(!this.certificateData)return"No certificate data available";const t=this.certificateData;return`
2
+ Certificate Details:
3
+ - Certificate #: ${t.certificate_number}
4
+ - Username: ${t.username}
5
+ - Percentage: ${t.percentage}%
6
+ - Creation Date: ${t.creationDate}
7
+ - Country: ${t.country} (${t.countryCode})
8
+ `.trim()}reset(){this.certificateData=null,this.verificationKey=null,this.localData=null,this.isValid=!1,this.nskdLbrLog("Certificate data reset","info")}readFileAsArrayBuffer(t){return new Promise((e,r)=>{const i=new FileReader;i.onload=a=>e(a.target.result),i.onerror=()=>r(new Error("Error reading file")),i.readAsArrayBuffer(t)})}async extractTextFromPng(t){try{const e=new Uint8Array(t);if(!(e[0]===137&&e[1]===80&&e[2]===78&&e[3]===71))throw new Error("Not a valid PNG file");let r=8,i=null;for(;r<e.length-12;){const a=e[r]<<24|e[r+1]<<16|e[r+2]<<8|e[r+3];if(String.fromCharCode(e[r+4],e[r+5],e[r+6],e[r+7])==="tEXt"){const l=e.slice(r+8,r+8+a),s=new TextDecoder("utf-8").decode(l),o=s.indexOf("\0");if(o!==-1){const n=s.substring(0,o),d=s.substring(o+1);if(n==="noskid-key"){i=d;break}}}r+=8+a+4}return i?(this.nskdLbrLog("Certificate data extracted successfully from PNG","success"),i):(this.nskdLbrLog("No 'noskid-key' text chunk found in PNG","error"),null)}catch(e){return this.nskdLbrLog(`Error extracting text from PNG: ${e.message}`,"error"),null}}extractVerificationKey(t){try{const e=/-*BEGIN NOSKID KEY-*\s*([a-f0-9]{64})/i,r=t.match(e);return r?r[1].toLowerCase():null}catch(e){return this.nskdLbrLog(`Error extracting verification key: ${e.message}`,"error"),null}}extractLocalData(t){try{const e=/-----BEGIN NOSKID KEY-----\s*([a-f0-9]+)\s*([A-Za-z0-9+/=]+)\s*([A-Za-z0-9+/=]+)\s*-----END NOSKID KEY-----/,r=t.match(e);if(!r)return null;const i=r[2],c=atob(i.replace(/=/g,"")).match(/CERT-\d+-(.+)/),l=c?c[1]:null,s=r[3],n=atob(s.replace(/=/g,"")).match(/CREATED-(.+)/),d=n?n[1]:null;return{username:l,creationDate:d}}catch(e){return this.nskdLbrLog(`Error extracting local data: ${e.message}`,"error"),null}}async verifyWithAPI(){try{this.nskdLbrLog("Verifying certificate with server...","info");const t=new AbortController,e=setTimeout(()=>t.abort(),this.timeout),r=await fetch(`${this.apiUrl}?key=${encodeURIComponent(this.verificationKey)}`,{signal:t.signal,headers:{"User-Agent":"NskdLbr/1.0.0"}});if(clearTimeout(e),!r.ok)throw new Error(`HTTP ${r.status}: ${r.statusText}`);const i=await r.json();if(!i.success)return this.isValid=!1,this.nskdLbrLog(`Certificate verification failed: ${i.message}`,"error"),{valid:!1,message:i.message,cached:i.cached||!1};if(this.localData&&this.strictCheck){const a=this.compareData(this.localData,i.data);if(!a.valid)return this.isValid=!1,this.nskdLbrLog("Certificate data mismatch!","error"),this.nskdLbrLog(`Mismatch reason: ${a.reason}`,"error"),this.nskdLbrLog("Note: Strict checking is enabled. Set strictCheck to false to skip local data validation.","warning"),{valid:!1,message:`Data mismatch: ${a.reason}`,cached:i.cached||!1,strictCheck:!0};this.nskdLbrLog("Local data validation passed","success")}else this.localData&&!this.strictCheck&&this.nskdLbrLog("Strict checking disabled - skipping local data validation","warning");return this.isValid=!0,this.certificateData=i.data,this.nskdLbrLog("Certificate is VALID!","success"),{valid:!0,message:"Certificate verified successfully",data:i.data,cached:i.cached||!1,strictCheck:this.strictCheck}}catch(t){throw t.name==="AbortError"?new Error("Request timeout - server took too long to respond"):new Error(`API verification failed: ${t.message}`)}}compareData(t,e){if(!t||!e)return{valid:!1,reason:"Missing data for comparison"};if(t.username!==e.username)return{valid:!1,reason:`Username mismatch: Local=${t.username}, API=${e.username}`};const r=t.creationDate.substring(0,16),i=e.creationDate.substring(0,16);return r!==i?{valid:!1,reason:`Creation date mismatch: Local=${r}, API=${i}`}:{valid:!0}}}typeof module<"u"&&module.exports?module.exports=NskdLbr:window.NskdLbr=NskdLbr;