Closed bjesuiter closed 1 year ago
Gosh this is Hard. I tried to figure it out, but nobody seems to have done it with WebCrypt publicly yet.
const secrets = await secretsPromise;
const telegramToken = z.string().parse(secrets.get("TELEGRAM_TOKEN"));
const encoder = new TextEncoder();
// hash telegram token with sha256
const telegramTokenHashed = await crypto.subtle.digest(
"SHA-256",
encoder.encode(telegramToken),
).then((hash) => new Uint8Array(hash));
//debug, skip this.
const tokenShaHex = Array.from(new Uint8Array(signature))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
console.log(tokenShaHex)
// Telegram Token Hash is correct, confirmed with sha256sum on terminal
tokenShaHex is the same as echo -n "telegramToken"|sha256sum
// generate hmac from hash and authDataCheckString
const hmac = await crypto.subtle.importKey(
"raw",
telegramTokenHashed,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
const signature = await crypto.subtle.sign(
"HMAC",
hmac,
encoder.encode(authDataCheckString),
);
const signatureHex = Array.from(new Uint8Array(signature))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
if (signatureHex !== payload.data.hash) {
console.log("signatureHex ", signatureHex);
console.log("payload.data.hash ", payload.data.hash);
const msg = `Problem while verifying oauth callback payload`;
return new Response(msg, { status: 403 });
}
function buildAuthString(payload: UserSession) {
const authStringArray = [];
authStringArray.push(`auth_date=${payload.unixAuthDate}`);
payload.firstName && authStringArray.push(`first_name=${payload.firstName}`);
authStringArray.push(`id=${payload.userId}`);
payload.lastName && authStringArray.push(`last_name=${payload.lastName}`);
payload.photoUrl && authStringArray.push(`photo_url=${payload.photoUrl}`);
payload.username && authStringArray.push(`username=${payload.username}`);
return authStringArray.join("\n");
}
Either
Trying to Verify the UserSession.Hash via echo -n "{parsedUserSessionString with \n between each line, but not at the end}"|openssl sha256 -hmac "sha256sum(TelegramToken)"
did not yield any useful results.
Could not verify my own callback URL after succesfull login.
The research looks good. Think we have to go over the algortihm together again to find the problem. I also think the problem is with encoding the Params in the thing which gets hashed. Probably our zod validation adds more properties into the object than should be there or otherwise messes with the hashing, so that our comparisons do not match.
Let's see...
let responseString = "";
const encoder = new TextEncoder();
crypto.subtle.importKey(
"raw",
encoder.encode("secret"),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
).then((hmac) => {
responseString = `
Has crypto: ${crypto !== undefined}, ${crypto}
Has crypto.subtle: ${crypto.subtle !== undefined}, ${crypto.subtle}
`;
return crypto.subtle.sign(
"HMAC",
hmac,
encoder.encode("Hello World!"),
);
}).then((payloadSignature) => {
responseString += `
payloadSignature: ${payloadSignature}
`;
const signatureHex = Array.from(new Uint8Array(payloadSignature))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
responseString += `
payloadSignature as Hex: ${signatureHex}
`;
}).then(() => {
console.log(responseString);
});
Result hash: 6fa7b4dea28ee348df10f9bb595ad985ff150a4adfd6131cca677d9acee07dc6
$ echo -n "Hello World!" | openssl sha256 -hmac "secret"
SHA256(stdin)= 6fa7b4dea28ee348df10f9bb595ad985ff150a4adfd6131cca677d9acee07dc6
let responseString = "";
const encoder = new TextEncoder();
crypto.subtle.digest("SHA-256", encoder.encode("secret"))
.then((secretHash) => {
return crypto.subtle.importKey(
"raw",
secretHash,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
})
.then((hmac) => {
responseString = `
Has crypto: ${crypto !== undefined}, ${crypto}
Has crypto.subtle: ${crypto.subtle !== undefined}, ${crypto.subtle}
`;
return crypto.subtle.sign(
"HMAC",
hmac,
encoder.encode("Hello World!"),
);
}).then((payloadSignature) => {
responseString += `
payloadSignature: ${payloadSignature}
`;
const signatureHex = Array.from(new Uint8Array(payloadSignature))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
responseString += `
payloadSignature as Hex: ${signatureHex}
`;
}).then(() => {
console.log(responseString);
});
Result Hash: 69a49567d6b9d862eb301f392557ce60bedf0dff3b1589b88c3051812373af7c
$ echo -n "Hello World!" | openssl sha256 -mac hmac -macopt hexkey:$(echo -n "secret"|sha256sum)
SHA256(stdin)= 69a49567d6b9d862eb301f392557ce60bedf0dff3b1589b88c3051812373af7c
Fixed by #31
When receiving a telegram auth token, we have to verify it in
/auth/callback.tsx
.Docs
Related