Buckets:
| import { ECDSAPoint } from "./curve.js"; | |
| import { euclideanMod, inverseMod, tonelliShanks } from "./math.js"; | |
| import { bigIntBytes, bigIntFromBytes } from "@oslojs/binary"; | |
| import { ASN1BitString, ASN1EncodableSequence, ASN1Integer, ASN1ObjectIdentifier, encodeASN1, encodeObjectIdentifier, parseASN1NoLeftoverBytes } from "@oslojs/asn1"; | |
| export function verifyECDSASignature(publicKey, hash, signature) { | |
| const q = new ECDSAPoint(publicKey.x, publicKey.y); | |
| if (!publicKey.curve.isOnCurve(q)) { | |
| return false; | |
| } | |
| if (publicKey.curve.multiply(publicKey.curve.n, q) !== null) { | |
| return false; | |
| } | |
| const e = hash.slice(0, publicKey.curve.size); | |
| const u1 = euclideanMod(bigIntFromBytes(e) * inverseMod(signature.s, publicKey.curve.n), publicKey.curve.n); | |
| const u1G = publicKey.curve.multiply(u1, publicKey.curve.g); | |
| if (u1G === null) { | |
| return false; | |
| } | |
| const u2 = euclideanMod(signature.r * inverseMod(signature.s, publicKey.curve.n), publicKey.curve.n); | |
| const u2Q = publicKey.curve.multiply(u2, q); | |
| if (u2Q === null) { | |
| return false; | |
| } | |
| const coord1 = publicKey.curve.add(u1G, u2Q); | |
| if (coord1 === null) { | |
| return false; | |
| } | |
| return euclideanMod(signature.r, publicKey.curve.n) === coord1.x; | |
| } | |
| export class ECDSAPublicKey { | |
| curve; | |
| x; | |
| y; | |
| constructor(curve, x, y) { | |
| this.curve = curve; | |
| this.x = x; | |
| this.y = y; | |
| } | |
| isCurve(curve) { | |
| return this.curve.objectIdentifier === curve.objectIdentifier; | |
| } | |
| encodeSEC1Uncompressed() { | |
| const bytes = new Uint8Array(1 + this.curve.size * 2); | |
| bytes[0] = 0x04; | |
| const xBytes = bigIntBytes(this.x); | |
| const yBytes = bigIntBytes(this.y); | |
| bytes.set(xBytes, 1 + this.curve.size - xBytes.byteLength); | |
| bytes.set(yBytes, 1 + this.curve.size); | |
| return bytes; | |
| } | |
| encodeSEC1Compressed() { | |
| const bytes = new Uint8Array(1 + this.curve.size); | |
| if (this.y % 2n === 0n) { | |
| bytes[0] = 0x02; | |
| } | |
| else { | |
| bytes[0] = 0x03; | |
| } | |
| const xBytes = bigIntBytes(this.x); | |
| bytes.set(xBytes, 1 + this.curve.size - xBytes.byteLength); | |
| return bytes; | |
| } | |
| encodePKIXUncompressed() { | |
| const algorithmIdentifier = new ASN1EncodableSequence([ | |
| new ASN1ObjectIdentifier(encodeObjectIdentifier("1.2.840.10045.2.1")), | |
| new ASN1ObjectIdentifier(encodeObjectIdentifier(this.curve.objectIdentifier)) | |
| ]); | |
| const encoded = this.encodeSEC1Uncompressed(); | |
| const subjectPublicKey = new ASN1BitString(encoded, encoded.byteLength * 8); | |
| const subjectPublicKeyInfo = new ASN1EncodableSequence([algorithmIdentifier, subjectPublicKey]); | |
| return encodeASN1(subjectPublicKeyInfo); | |
| } | |
| encodePKIXCompressed() { | |
| const algorithmIdentifier = new ASN1EncodableSequence([ | |
| new ASN1ObjectIdentifier(encodeObjectIdentifier("1.2.840.10045.2.1")), | |
| new ASN1ObjectIdentifier(encodeObjectIdentifier(this.curve.objectIdentifier)) | |
| ]); | |
| const encoded = this.encodeSEC1Compressed(); | |
| const subjectPublicKey = new ASN1BitString(encoded, encoded.byteLength * 8); | |
| const subjectPublicKeyInfo = new ASN1EncodableSequence([algorithmIdentifier, subjectPublicKey]); | |
| return encodeASN1(subjectPublicKeyInfo); | |
| } | |
| } | |
| export function decodeSEC1PublicKey(curve, bytes) { | |
| if (bytes.byteLength < 1) { | |
| throw new Error("Invalid public key"); | |
| } | |
| if (bytes[0] === 0x04) { | |
| if (bytes.byteLength !== curve.size * 2 + 1) { | |
| throw new Error("Invalid public key"); | |
| } | |
| const x = bigIntFromBytes(bytes.slice(1, curve.size + 1)); | |
| const y = bigIntFromBytes(bytes.slice(curve.size + 1)); | |
| return new ECDSAPublicKey(curve, x, y); | |
| } | |
| if (bytes[0] === 0x02) { | |
| if (bytes.byteLength !== curve.size + 1) { | |
| throw new Error("Invalid public key"); | |
| } | |
| const x = bigIntFromBytes(bytes.slice(1)); | |
| const y2 = euclideanMod(x ** 3n + curve.a * x + curve.b, curve.p); | |
| const y = tonelliShanks(y2, curve.p); | |
| if (y % 2n === 0n) { | |
| return new ECDSAPublicKey(curve, x, y); | |
| } | |
| return new ECDSAPublicKey(curve, x, curve.p - y); | |
| } | |
| if (bytes[0] === 0x03) { | |
| if (bytes.byteLength !== curve.size + 1) { | |
| throw new Error("Invalid public key"); | |
| } | |
| const x = bigIntFromBytes(bytes.slice(1)); | |
| const y2 = euclideanMod(x ** 3n + curve.a * x + curve.b, curve.p); | |
| const y = tonelliShanks(y2, curve.p); | |
| if (y % 2n === 1n) { | |
| return new ECDSAPublicKey(curve, x, y); | |
| } | |
| return new ECDSAPublicKey(curve, x, curve.p - y); | |
| } | |
| throw new Error("Unknown encoding format"); | |
| } | |
| export class ECDSASignature { | |
| r; | |
| s; | |
| constructor(r, s) { | |
| if (r < 1n || s < 1n) { | |
| throw new TypeError("Invalid signature"); | |
| } | |
| this.r = r; | |
| this.s = s; | |
| } | |
| encodeIEEEP1363(curve) { | |
| const rs = new Uint8Array(curve.size * 2); | |
| const rBytes = bigIntBytes(this.r); | |
| if (rBytes.byteLength > curve.size) { | |
| throw new Error("'r' is too large"); | |
| } | |
| const sBytes = bigIntBytes(this.s); | |
| if (rBytes.byteLength > curve.size) { | |
| throw new Error("'s' is too large"); | |
| } | |
| rs.set(rBytes, curve.size - rBytes.byteLength); | |
| rs.set(sBytes, rs.byteLength - sBytes.byteLength); | |
| return rs; | |
| } | |
| encodePKIX() { | |
| const asn1 = new ASN1EncodableSequence([new ASN1Integer(this.r), new ASN1Integer(this.s)]); | |
| return encodeASN1(asn1); | |
| } | |
| } | |
| export function decodeIEEEP1363ECDSASignature(curve, bytes) { | |
| if (bytes.byteLength !== curve.size * 2) { | |
| throw new Error("Failed to decode signature: Invalid signature size"); | |
| } | |
| const r = bigIntFromBytes(bytes.slice(0, curve.size)); | |
| const s = bigIntFromBytes(bytes.slice(curve.size)); | |
| return new ECDSASignature(r, s); | |
| } | |
| export function decodePKIXECDSASignature(der) { | |
| try { | |
| const sequence = parseASN1NoLeftoverBytes(der).sequence(); | |
| return new ECDSASignature(sequence.at(0).integer().value, sequence.at(1).integer().value); | |
| } | |
| catch { | |
| throw new Error("Failed to decode signature"); | |
| } | |
| } | |
| export function decodePKIXECDSAPublicKey(bytes, curves) { | |
| let algorithmIdentifierObjectIdentifier; | |
| let algorithmIdentifierParameter; | |
| let asn1PublicKey; | |
| try { | |
| const subjectPublicKeyInfo = parseASN1NoLeftoverBytes(bytes).sequence(); | |
| const algorithmIdentifier = subjectPublicKeyInfo.at(0).sequence(); | |
| algorithmIdentifierObjectIdentifier = algorithmIdentifier.at(0).objectIdentifier(); | |
| algorithmIdentifierParameter = algorithmIdentifier.at(1).objectIdentifier(); | |
| asn1PublicKey = subjectPublicKeyInfo.at(1).bitString(); | |
| } | |
| catch { | |
| throw new Error("Failed to decode elliptic curve public key"); | |
| } | |
| if (!algorithmIdentifierObjectIdentifier.is("1.2.840.10045.2.1")) { | |
| throw new Error("Invalid algorithm"); | |
| } | |
| for (const curve of curves) { | |
| if (algorithmIdentifierParameter.is(curve.objectIdentifier)) { | |
| return decodeSEC1PublicKey(curve, asn1PublicKey.bytes); | |
| } | |
| } | |
| throw new Error("No matching curves"); | |
| } | |
Xet Storage Details
- Size:
- 7.43 kB
- Xet hash:
- b64c405234fa700754bfe983b4c9c389a860c583caaf79c69884479c591df8de
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.