forked from algorandfoundation/liquid-auth-js
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathencoding.ts
More file actions
166 lines (150 loc) · 5.23 KB
/
encoding.ts
File metadata and controls
166 lines (150 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import nacl from 'tweetnacl';
import { decodeAsBytes, encodeBytes } from './hi-base32.js';
import { createMethod } from './sha512.js';
const sha512_256 = createMethod(256);
const chars =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
const ALGORAND_PUBLIC_KEY_BYTE_LENGTH = 32;
const ALGORAND_ADDRESS_BYTE_LENGTH = 36;
const ALGORAND_CHECKSUM_BYTE_LENGTH = 4;
const ALGORAND_ADDRESS_LENGTH = 58;
const HASH_BYTES_LENGTH = 32;
/**
* A constant string containing the error message for a malformed address.
* This message is used to indicate that an address does not adhere to the expected format or structure.
*
* @protected
*/
export const MALFORMED_ADDRESS_ERROR_MSG = 'Malformed address';
/**
* A constant string that represents an error message for an Algorand address with a bad checksum.
* This message is used to indicate that the checksum of the provided Algorand address is invalid,
* which typically occurs when the address is incorrectly formed or corrupted.
*
* @protected
*/
export const ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG = 'Bad checksum';
/**
* A constant string representing an error message for invalid base64url input.
* This value is used to indicate that the provided input does not conform to the
* expected base64url format or specification.
*
* @protected
*/
export const INVALID_BASE64URL_INPUT = 'Invalid base64url input';
/**
* Converts a given Uint8Array or ArrayBuffer to a Base64 URL-safe encoded string.
*
* @param {Uint8Array | ArrayBuffer} arr - The input data to be converted to a Base64 URL-safe string.
* @return {string} A Base64 URL-safe encoded string representation of the input data.
*/
export function toBase64URL(arr: Uint8Array | ArrayBuffer): string {
const bytes = arr instanceof Uint8Array ? arr : new Uint8Array(arr);
const len = bytes.length;
let base64 = '';
for (let i = 0; i < len; i += 3) {
base64 += chars[bytes[i] >> 2];
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
base64 += chars[bytes[i + 2] & 63];
}
if (len % 3 === 2) {
base64 = base64.substring(0, base64.length - 1);
} else if (len % 3 === 1) {
base64 = base64.substring(0, base64.length - 2);
}
return base64;
}
/**
* Converts a Base64 URL-encoded string into a Uint8Array.
*
* @param {string} base64url - The Base64 URL-encoded string to be converted.
* This string must be a valid Base64 URL-safe format.
* @return {Uint8Array} A Uint8Array representing the decoded binary data from the input Base64 URL string.
* @throws {Error} If the provided input is not a string or is an invalid Base64 URL format.
*/
export function fromBase64Url(base64url: string): Uint8Array {
if (typeof base64url !== 'string') {
throw new Error(INVALID_BASE64URL_INPUT);
}
return new Uint8Array(
// TODO: Cross-platform solution since atob is deprecated in Node
atob(base64url.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''))
.split('')
.map((c) => c.charCodeAt(0)),
);
}
function concatArrays(...arrs: Array<ArrayLike<number>>) {
const size = arrs.reduce((sum, arr) => sum + arr.length, 0);
const c = new Uint8Array(size);
let offset = 0;
for (let i = 0; i < arrs.length; i++) {
c.set(arrs[i], offset);
offset += arrs[i].length;
}
return c;
}
/**
* Encodes a given address into a string representation, including its checksum.
*
* @param {Uint8Array} address The public key to be encoded into an address.
* @return {string} The encoded address as a string representation.
*
* @deprecated - use algo-models or algokit-utils
*/
export function encodeAddress(address: Uint8Array) {
// compute checksum
const checksum = sha512_256
.array(address)
.slice(
nacl.sign.publicKeyLength - ALGORAND_CHECKSUM_BYTE_LENGTH,
nacl.sign.publicKeyLength,
);
const addr = encodeBytes(concatArrays(address, checksum));
return addr.toString().slice(0, ALGORAND_ADDRESS_LENGTH); // removing the extra '===='
}
/**
* decodeAddress takes an Algorand address in string form and decodes it into a Uint8Array.
* @param address - an Algorand address with checksum.
* @returns the decoded form of the address's public key and checksum
*
* @deprecated use algo-models or algokit-utils
*/
export function decodeAddress(address: string): Uint8Array {
if (
typeof address !== 'string' ||
address.length !== ALGORAND_ADDRESS_LENGTH
) {
throw new Error(MALFORMED_ADDRESS_ERROR_MSG);
}
// try to decode
const decoded = decodeAsBytes(address.toString());
// Find publickey and checksum
const pk = new Uint8Array(
decoded.slice(
0,
ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH,
),
);
const cs = new Uint8Array(
decoded.slice(
ALGORAND_PUBLIC_KEY_BYTE_LENGTH,
ALGORAND_ADDRESS_BYTE_LENGTH,
),
);
// Compute checksum
const checksum = sha512_256
.array(pk)
.slice(
HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH,
HASH_BYTES_LENGTH,
);
// Check if the checksum and the address are equal
if (
checksum.length !== cs.length ||
!Array.from(checksum).every((val, i) => val === cs[i])
) {
throw new Error(ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG);
}
return pk;
}