Skip to content

Commit

Permalink
Merge pull request #103 from PeculiarVentures/ecc-rfc3279
Browse files Browse the repository at this point in the history
Update ECC module with RFC3279 support
  • Loading branch information
microshine authored Sep 16, 2024
2 parents c04cfbf + eb9d4a8 commit 687d8ad
Show file tree
Hide file tree
Showing 8 changed files with 813 additions and 507 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,4 @@
"@types/rimraf": "^4.0.5",
"rimraf": "^5.0.5"
}
}
}
11 changes: 8 additions & 3 deletions packages/ecc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/PeculiarVentures/asn1-schema/master/packages/ecc/LICENSE.md)
[![npm version](https://badge.fury.io/js/%40peculiar%2Fasn1-ecc.svg)](https://badge.fury.io/js/%40peculiar%2Fasn1-ecc)

[![NPM](https://nodei.co/npm/@peculiar/asn1-ecc.png)](https://nodei.co/npm/@peculiar/asn1-ecc/)

[RFC 5915](https://tools.ietf.org/html/rfc5915) Elliptic Curve Private Key Structure
[RFC 5480](https://tools.ietf.org/html/rfc5480) Elliptic Curve Cryptography Subject Public Key Information
This module provides ASN.1 schema definitions and parsers for Elliptic Curve Cryptography (ECC) structures. It includes support for various ECC-related standards and specifications, making it easier to work with ECC keys and certificates in JavaScript and TypeScript applications.

| Document | Description |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| [RFC 5915](https://tools.ietf.org/html/rfc5915) | Elliptic Curve Private Key Structure |
| [RFC 5480](https://tools.ietf.org/html/rfc5480) | Elliptic Curve Cryptography Subject Public Key Information |
| [RFC 3279](https://datatracker.ietf.org/doc/html/rfc3279#section-2.3.5) | Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate: ECDSA and ECDH Keys |
8 changes: 6 additions & 2 deletions packages/ecc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
},
"homepage": "https://github.com/PeculiarVentures/asn1-schema/tree/master/packages/ecc#readme",
"keywords": [
"asn"
"asn",
"ecc",
"rfc5915",
"rfc5480",
"rfc3279"
],
"author": "PeculiarVentures, LLC",
"license": "MIT",
Expand All @@ -39,4 +43,4 @@
"asn1js": "^3.0.5",
"tslib": "^2.6.2"
}
}
}
11 changes: 9 additions & 2 deletions packages/ecc/src/ec_parameters.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { AsnType, AsnTypeTypes, AsnProp, AsnPropTypes } from "@peculiar/asn1-schema";
import { SpecifiedECDomain } from "./rfc3279";

/**
* ```
* ECParameters ::= CHOICE {
* namedCurve OBJECT IDENTIFIER
* -- implicitCurve NULL
* -- specifiedCurve SpecifiedECDomain
* implicitCurve NULL
* specifiedCurve SpecifiedECDomain
* }
* -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
* -- Details for SpecifiedECDomain can be found in [X9.62].
Expand All @@ -19,6 +20,12 @@ export class ECParameters {
@AsnProp({ type: AsnPropTypes.ObjectIdentifier })
public namedCurve?: string;

@AsnProp({ type: AsnPropTypes.Null })
public implicitCurve?: null;

@AsnProp({ type: SpecifiedECDomain })
public specifiedCurve?: SpecifiedECDomain;

constructor(params: Partial<ECParameters> = {}) {
Object.assign(this, params);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/ecc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from "./algorithms";
export * from "./ec_parameters";
export * from "./ec_private_key";
export * from "./ec_signature_value";
export * from "./object_identifiers";
export * from "./object_identifiers";
export * from "./rfc3279";
108 changes: 108 additions & 0 deletions packages/ecc/src/rfc3279.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { AsnType, AsnTypeTypes, AsnProp, AsnPropTypes, OctetString, AsnIntegerArrayBufferConverter } from "@peculiar/asn1-schema";

/**
* ```asn1
* FieldID ::= SEQUENCE {
* fieldType OBJECT IDENTIFIER,
* parameters ANY DEFINED BY fieldType }
* ```
*/
@AsnType({ type: AsnTypeTypes.Sequence })
export class FieldID {
@AsnProp({ type: AsnPropTypes.ObjectIdentifier })
public fieldType!: string;

@AsnProp({ type: AsnPropTypes.Any })
public parameters!: ArrayBuffer;

constructor(params: Partial<FieldID> = {}) {
Object.assign(this, params);
}
}

/**
* ```asn1
* ECPoint ::= OCTET STRING
* ```
*/
export class ECPoint extends OctetString { }

/**
* ```asn1
* FieldElement ::= OCTET STRING
* ```
*/
export class FieldElement extends OctetString { }

/**
* ```asn1
* Curve ::= SEQUENCE {
* a FieldElement,
* b FieldElement,
* seed BIT STRING OPTIONAL }
* ```
*/
@AsnType({ type: AsnTypeTypes.Sequence })
export class Curve {
@AsnProp({ type: AsnPropTypes.OctetString })
public a!: ArrayBuffer;

@AsnProp({ type: AsnPropTypes.OctetString })
public b!: ArrayBuffer;

@AsnProp({ type: AsnPropTypes.BitString, optional: true })
public seed?: ArrayBuffer;

constructor(params: Partial<Curve> = {}) {
Object.assign(this, params);
}
}

/**
* ```asn1
* ECPVer ::= INTEGER {ecpVer1(1)}
* ```
*/
export enum ECPVer {
ecpVer1 = 1,
}

/**
* ```asn1
* SpecifiedECDomain ::= SEQUENCE {
* version ECPVer, -- version is always 1
* fieldID FieldID, -- identifies the finite field over
* -- which the curve is defined
* curve Curve, -- coefficients a and b of the
* -- elliptic curve
* base ECPoint, -- specifies the base point P
* -- on the elliptic curve
* order INTEGER, -- the order n of the base point
* cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n
* }
* ```
*/
@AsnType({ type: AsnTypeTypes.Sequence })
export class SpecifiedECDomain {
@AsnProp({ type: AsnPropTypes.Integer })
public version: ECPVer = ECPVer.ecpVer1;

@AsnProp({ type: FieldID })
public fieldID!: FieldID;

@AsnProp({ type: Curve })
public curve!: Curve;

@AsnProp({ type: ECPoint })
public base!: ECPoint;

@AsnProp({ type: AsnPropTypes.Integer, converter: AsnIntegerArrayBufferConverter })
public order!: ArrayBuffer;

@AsnProp({ type: AsnPropTypes.Integer, optional: true })
public cofactor?: ArrayBuffer;

constructor(params: Partial<SpecifiedECDomain> = {}) {
Object.assign(this, params);
}
}
65 changes: 54 additions & 11 deletions packages/ecc/test/ec_parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,62 @@ import { AsnConvert } from "@peculiar/asn1-schema";

context("EC parameters", () => {

const ecParamsHex = "06082a8648ce3d030107";

it("serialize", () => {
const ecParams = new ECParameters({
namedCurve: id_secp256r1,
})
const der = AsnConvert.serialize(ecParams);
assert.strictEqual(Buffer.from(der).toString("hex"), ecParamsHex);
context("Named curve", () => {

const ecParamsHex = "06082a8648ce3d030107";

it("serialize", () => {
const ecParams = new ECParameters({
namedCurve: id_secp256r1,
});
const der = AsnConvert.serialize(ecParams);
assert.strictEqual(Buffer.from(der).toString("hex"), ecParamsHex);
});

it("parse", () => {
const ecParams = AsnConvert.parse(Buffer.from(ecParamsHex, "hex"), ECParameters);
assert.strictEqual(ecParams.namedCurve, id_secp256r1);
});

});

it("parse", () => {
const ecParams = AsnConvert.parse(Buffer.from(ecParamsHex, "hex"), ECParameters);
assert.strictEqual(ecParams.namedCurve, id_secp256r1);
context("Specified curve", () => {
// Reference: https://github.com/PeculiarVentures/asn1-schema/issues/102

const ecParamsEnc = Buffer.from("MIH3AgEBMCwGByqGSM49AQECIQD_____AAAAAQAAAAAAAAAAAAAAAP_______________zBbBCD_____AAAAAQAAAAAAAAAAAAAAAP_______________AQgWsY12Ko6k-ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9-kARBBGsX0fLhLEJH-Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT-NC4v4af5uO5-tKfA-eFivOM1drMV7Oy7ZAaDe_UfUCIQD_____AAAAAP__________vOb6racXnoTzucrC_GMlUQIBAQ", "base64url");

it("parse/serialize", () => {
const ecParams = AsnConvert.parse(ecParamsEnc, ECParameters);
assert.ok(ecParams.specifiedCurve);
const specifiedCurve = ecParams.specifiedCurve;

// Check version
assert.strictEqual(specifiedCurve.version, 1);

// Check fieldID
const fieldID = specifiedCurve.fieldID;
assert.strictEqual(fieldID.fieldType, '1.2.840.10045.1.1');
assert.strictEqual(fieldID.parameters.byteLength, 35);

// Check curve
const curve = specifiedCurve.curve;
assert.strictEqual(curve.a.byteLength, 32);
assert.strictEqual(curve.b.byteLength, 32);
assert.strictEqual(curve.seed!.byteLength, 20);

// Check base
assert.strictEqual(specifiedCurve.base.byteLength, 65);

// Check order
assert.strictEqual(specifiedCurve.order.byteLength, 33);

// Check cofactor
assert.strictEqual(specifiedCurve.cofactor, 1);

// Serialize and compare
const der = AsnConvert.serialize(ecParams);
assert.strictEqual(Buffer.from(der).compare(ecParamsEnc), 0, "Encoded EC parameters are not equal");
});
});

});
Loading

0 comments on commit 687d8ad

Please sign in to comment.