download
raw
6.47 kB
import { bigIntFromBytes, concatenateBytes, DynamicBuffer } from "@oslojs/binary";
import { constantTimeEqual } from "../subtle/index.js";
import { ASN1BitString, ASN1EncodableSequence, ASN1Integer, ASN1Null, ASN1ObjectIdentifier, ASN1OctetString, ASN1UniversalType, encodeASN1, encodeObjectIdentifier, parseASN1NoLeftoverBytes } from "@oslojs/asn1";
export function verifyRSASSAPKCS1v15Signature(publicKey, hashObjectIdentifier, hashed, signature) {
const s = bigIntFromBytes(signature);
const m = powmod(s, publicKey.e, publicKey.n);
const em = new Uint8Array(Math.ceil((publicKey.n.toString(2).length - 1) / 8));
for (let i = 0; i < em.byteLength; i++) {
em[i] = Number((m >> BigInt((em.byteLength - i - 1) * 8)) & 0xffn);
}
const t = encodeASN1(new ASN1EncodableSequence([
new ASN1EncodableSequence([
new ASN1ObjectIdentifier(encodeObjectIdentifier(hashObjectIdentifier)),
new ASN1Null()
]),
new ASN1OctetString(hashed)
]));
if (em.byteLength < t.byteLength + 11) {
return false;
}
const ps = new Uint8Array(em.byteLength - t.byteLength - 3).fill(0xff);
const emPrime = new DynamicBuffer(0);
emPrime.writeByte(0x00);
emPrime.writeByte(0x01);
emPrime.write(ps);
emPrime.writeByte(0x00);
emPrime.write(t);
return constantTimeEqual(em, emPrime.bytes());
}
export function verifyRSASSAPSSSignature(publicKey, MessageHashAlgorithm, MGF1HashAlgorithm, saltLength, hashed, signature) {
const s = bigIntFromBytes(signature);
if (s < 0 || s >= publicKey.n) {
return false;
}
const m = powmod(s, publicKey.e, publicKey.n);
const maximalEMBits = publicKey.n.toString(2).length - 1;
const em = new Uint8Array(Math.ceil(maximalEMBits / 8));
for (let i = 0; i < em.byteLength; i++) {
em[i] = Number((m >> BigInt((em.byteLength - i - 1) * 8)) & 0xffn);
}
if (em.byteLength < hashed.byteLength + saltLength + 2) {
return false;
}
if (em[em.byteLength - 1] !== 0xbc) {
return false;
}
const db = em.slice(0, em.byteLength - hashed.byteLength - 1);
const h = em.slice(em.byteLength - hashed.byteLength - 1, em.byteLength - 1);
if (db[0] >> (8 - (8 * em.byteLength - maximalEMBits)) !== 0) {
return false;
}
const dbMask = mgf1(MGF1HashAlgorithm, h, em.byteLength - hashed.byteLength - 1);
for (let i = 0; i < db.byteLength; i++) {
db[i] ^= dbMask[i];
}
for (let i = 0; i < Math.floor((em.byteLength - hashed.byteLength - 1) / 8); i++) {
db[i] = 0;
}
db[Math.floor((em.byteLength - hashed.byteLength - 1) / 8)] &=
(1 << (8 - ((em.byteLength - hashed.byteLength - 1) % 8))) - 1;
const salt = db.slice(db.byteLength - saltLength);
const mPrime = new DynamicBuffer(8 + hashed.byteLength + saltLength);
mPrime.write(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
mPrime.write(hashed);
mPrime.write(salt);
const hPrimeHash = new MessageHashAlgorithm();
hPrimeHash.update(mPrime.bytes());
return constantTimeEqual(h, hPrimeHash.digest());
}
export class RSAPublicKey {
n;
e;
constructor(n, e) {
this.n = n;
this.e = e;
}
encodePKCS1() {
const asn1 = new ASN1EncodableSequence([new ASN1Integer(this.n), new ASN1Integer(this.e)]);
return encodeASN1(asn1);
}
encodePKIX() {
const algorithmIdentifier = new ASN1EncodableSequence([
new ASN1ObjectIdentifier(encodeObjectIdentifier("1.2.840.113549.1.1.1")),
new ASN1Null()
]);
const encoded = this.encodePKCS1();
const subjectPublicKey = new ASN1BitString(encoded, encoded.byteLength * 8);
const subjectPublicKeyInfo = new ASN1EncodableSequence([algorithmIdentifier, subjectPublicKey]);
return encodeASN1(subjectPublicKeyInfo);
}
}
export function decodePKCS1RSAPublicKey(pkcs1) {
try {
const asn1PublicKey = parseASN1NoLeftoverBytes(pkcs1).sequence();
return new RSAPublicKey(asn1PublicKey.at(0).integer().value, asn1PublicKey.at(1).integer().value);
}
catch {
throw new Error("Invalid public key");
}
}
export function decodePKIXRSAPublicKey(pkix) {
let asn1Algorithm;
let asn1Parameter;
let asn1PublicKey;
try {
const asn1SubjectPublicKeyInfo = parseASN1NoLeftoverBytes(pkix).sequence();
const asn1AlgorithmIdentifier = asn1SubjectPublicKeyInfo.at(0).sequence();
asn1Algorithm = asn1AlgorithmIdentifier.at(0).objectIdentifier();
asn1Parameter = asn1AlgorithmIdentifier.at(1);
asn1PublicKey = asn1SubjectPublicKeyInfo.at(1).bitString();
}
catch {
throw new Error("Failed to parse SubjectPublicKeyInfo");
}
// TODO: Should other (more-specific) OIDs be supported?
if (!asn1Algorithm.is("1.2.840.113549.1.1.1")) {
throw new Error("Invalid public key OID");
}
if (asn1Parameter.universalType() !== ASN1UniversalType.Null) {
throw new Error("Invalid public key");
}
try {
return decodePKCS1RSAPublicKey(asn1PublicKey.bytes);
}
catch {
throw new Error("Invalid public key");
}
}
export const sha1ObjectIdentifier = "1.3.14.3.2.26";
export const sha224ObjectIdentifier = "2.16.840.1.101.3.4.2.4";
export const sha256ObjectIdentifier = "2.16.840.1.101.3.4.2.1";
export const sha384ObjectIdentifier = "2.16.840.1.101.3.4.2.2";
export const sha512ObjectIdentifier = "2.16.840.1.101.3.4.2.3";
function mgf1(Hash, Z, l) {
let t = new Uint8Array();
let counter = 0;
while (t.byteLength < l) {
const counterBytes = new Uint8Array(4);
for (let j = 0; j < counterBytes.byteLength; j++) {
counterBytes[j] = Number((counter >> ((counterBytes.byteLength - j - 1) * 8)) & 0xff);
}
const zcHash = new Hash();
zcHash.update(Z);
zcHash.update(counterBytes);
t = concatenateBytes(t, zcHash.digest());
counter++;
}
return t.slice(0, l);
}
function powmod(x, y, p) {
let res = 1n; // Initialize result
x = x % p;
while (y > 0) {
if (y % 2n === 1n) {
res = euclideanMod(res * x, p);
}
y = y >> 1n;
x = euclideanMod(x * x, p);
}
return res;
}
function euclideanMod(x, y) {
const r = x % y;
if (r < 0n) {
return r + y;
}
return r;
}

Xet Storage Details

Size:
6.47 kB
·
Xet hash:
5f6d8e02b14a0c466d5d2071ac220189b4e23dbe7decc85ca40014a6eea374be

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.