@@ -3,34 +3,32 @@ import { asBufferSource, assert, fromHex, toHex } from "@ndn/util";
33import * as asn1 from "@yoursunny/asn1" ;
44
55import type { CryptoAlgorithm , SigningAlgorithm } from "../key/mod" ;
6- import { assertSpkiAlgorithm } from "./impl-spki " ;
6+ import { assertSpkiAlgorithm , toPkcs8 } from "./impl-asn1 " ;
77
88const SignVerifyParams : EcdsaParams = { name : "ECDSA" , hash : "SHA-256" } ;
99
1010function makeGenParams ( curve : EcCurve ) : EcKeyGenParams & EcKeyImportParams {
1111 return { name : "ECDSA" , namedCurve : curve } ;
1212}
1313
14- const PointSizes = {
15- "P-256" : 32 ,
16- "P-384" : 48 ,
17- "P-521" : 66 ,
14+ const EcPublicKeyOid = "2A8648CE3D0201" ; // 1.2.840.10045.2.1
15+
16+ const NamedCurveInfo = {
17+ "P-256" : [ 32 , "2A8648CE3D030107" ] , // 1.2.840.10045.3.1.7
18+ "P-384" : [ 48 , "2B81040022" ] , // 1.3.132.0.34
19+ "P-521" : [ 66 , "2B81040023" ] , // 1.3.132.0.35
1820} as const ;
1921
20- const NamedCurveOids : Record < string , EcCurve > = {
21- "2A8648CE3D030107" : "P-256" , // 1.2.840.10045.3.1.7
22- "2B81040022" : "P-384" , // 1.3.132.0.34
23- "2B81040023" : "P-521" , // 1.3.132.0.35
24- } ;
22+ const NamedCurveOids : Record < string , EcCurve > = Object . fromEntries ( Object . entries ( NamedCurveInfo ) . map ( ( [ curve , [ , oid ] ] ) => [ oid , curve as EcCurve ] ) ) ;
2523
26- export type EcCurve = keyof typeof PointSizes ;
24+ export type EcCurve = keyof typeof NamedCurveInfo ;
2725export namespace EcCurve {
2826 export const Default : EcCurve = "P-256" ;
29- export const Choices = Object . keys ( PointSizes ) as readonly EcCurve [ ] ;
27+ export const Choices = Object . keys ( NamedCurveInfo ) as readonly EcCurve [ ] ;
3028
3129 /** Detect EcCurve from SubjectPublicKeyInfo. */
3230 export function detectFromSpki ( der : asn1 . ElementBuffer ) : EcCurve {
33- assertSpkiAlgorithm ( der , "ECDSA" , "2A8648CE3D0201" ) ; // 1.2.840.10045.2.1
31+ assertSpkiAlgorithm ( der , "ECDSA" , EcPublicKeyOid ) ;
3432
3533 // SubjectPublicKeyInfo.algorithm.parameter
3634 const ecp = der . children ?. [ 0 ] ?. children ?. [ 1 ] ;
@@ -51,6 +49,26 @@ function toUintHex(array: Uint8Array): string {
5149 return toHex ( array . subarray ( msb ) ) ;
5250}
5351
52+ function stripSec1Parameters ( input : Uint8Array ) : Uint8Array {
53+ // https://datatracker.ietf.org/doc/html/rfc5915#section-3
54+ // Delete ECParameters field from SEC#1 data.
55+ const sec1 = asn1 . parseVerbose ( input ) ;
56+ let index : number ;
57+ if ( sec1 . children && ( index = sec1 . children . findIndex ( ( child ) => child . type === 160 ) ) >= 0 ) {
58+ sec1 . children . splice ( index , 1 ) ;
59+ }
60+ return asn1 . pack ( sec1 ) ;
61+ }
62+
63+ function loadPkcs8 ( pkcs8 : Uint8Array , spki : Uint8Array , curve : EcCurve , extractable : boolean ) {
64+ const params = makeGenParams ( curve ) ;
65+ return Promise . all ( [
66+ params , // eslint-disable-line @typescript-eslint/await-thenable
67+ crypto . subtle . importKey ( "pkcs8" , asBufferSource ( pkcs8 ) , params , extractable , ECDSA . keyUsages . private ) ,
68+ crypto . subtle . importKey ( "spki" , asBufferSource ( spki ) , params , true , ECDSA . keyUsages . public ) ,
69+ ] ) ;
70+ }
71+
5472/** Sha256WithEcdsa signing algorithm. */
5573export const ECDSA : SigningAlgorithm < ECDSA . Info , true , ECDSA . GenParams > = {
5674 uuid : "a81b3696-65e5-4f4c-bb45-14125472321b" ,
@@ -60,18 +78,25 @@ export const ECDSA: SigningAlgorithm<ECDSA.Info, true, ECDSA.GenParams> = {
6078 public : [ "verify" ] ,
6179 } ,
6280
63- async cryptoGenerate ( { curve, importPkcs8 } : ECDSA . GenParams , extractable : boolean ) {
81+ async cryptoGenerate ( { curve, importPkcs8, importSec1 } : ECDSA . GenParams , extractable : boolean ) {
6482 let params : ReturnType < typeof makeGenParams > ;
6583 let privateKey : CryptoKey ;
6684 let publicKey : CryptoKey ;
6785 if ( importPkcs8 ) {
6886 const [ pkcs8 , spki ] = importPkcs8 ;
6987 curve ??= EcCurve . detectFromSpki ( asn1 . parseVerbose ( spki ) ) ;
70- params = makeGenParams ( curve ) ;
71- [ privateKey , publicKey ] = await Promise . all ( [
72- crypto . subtle . importKey ( "pkcs8" , asBufferSource ( pkcs8 ) , params , extractable , this . keyUsages . private ) ,
73- crypto . subtle . importKey ( "spki" , asBufferSource ( spki ) , params , true , this . keyUsages . public ) ,
74- ] ) ;
88+ [ params , privateKey , publicKey ] = await loadPkcs8 ( pkcs8 , spki , curve , extractable ) ;
89+ } else if ( importSec1 ) {
90+ const [ sec1 , spki ] = importSec1 ;
91+ curve ??= EcCurve . detectFromSpki ( asn1 . parseVerbose ( spki ) ) ;
92+ const pkcs8 = toPkcs8 (
93+ [
94+ asn1 . Any ( "06" , EcPublicKeyOid ) , // OID
95+ asn1 . Any ( "06" , NamedCurveInfo [ curve ] [ 1 ] ) ,
96+ ] ,
97+ stripSec1Parameters ( sec1 ) ,
98+ ) ;
99+ [ params , privateKey , publicKey ] = await loadPkcs8 ( pkcs8 , spki , curve , extractable ) ;
75100 } else {
76101 curve ??= EcCurve . Default ;
77102 params = makeGenParams ( curve ) ;
@@ -105,7 +130,7 @@ export const ECDSA: SigningAlgorithm<ECDSA.Info, true, ECDSA.GenParams> = {
105130 makeLLSign ( { privateKey, info : { curve } } : CryptoAlgorithm . PrivateKey < ECDSA . Info > ) {
106131 return async ( input ) => {
107132 const raw = await crypto . subtle . sign ( SignVerifyParams , privateKey , asBufferSource ( input ) ) ;
108- const pointSize = PointSizes [ curve ] ;
133+ const pointSize = NamedCurveInfo [ curve ] [ 0 ] ;
109134 return fromHex ( asn1 . Any (
110135 "30" ,
111136 asn1 . UInt ( toUintHex ( new Uint8Array ( raw , 0 , pointSize ) ) ) ,
@@ -116,7 +141,7 @@ export const ECDSA: SigningAlgorithm<ECDSA.Info, true, ECDSA.GenParams> = {
116141
117142 makeLLVerify ( { publicKey, info : { curve } } : CryptoAlgorithm . PublicKey < ECDSA . Info > ) {
118143 return async ( input , sig ) => {
119- const pointSize = PointSizes [ curve ] ;
144+ const pointSize = NamedCurveInfo [ curve ] [ 0 ] ;
120145
121146 const der = asn1 . parseVerbose ( sig ) ;
122147 const r = der . children ?. [ 0 ] ?. value ;
@@ -153,6 +178,14 @@ export namespace ECDSA {
153178 * If {@link curve} is also specified, it must match the SPKI public key.
154179 */
155180 importPkcs8 ?: [ pkcs8 : Uint8Array , spki : Uint8Array ] ;
181+
182+ /**
183+ * Import SEC#1 private key and SPKI public key instead of generating.
184+ *
185+ * If {@link curve} is also specified, it must match the SPKI public key.
186+ * If {@link importPkcs8} is also specified, this field is ignored.
187+ */
188+ importSec1 ?: [ sec1 : Uint8Array , spki : Uint8Array ] ;
156189 }
157190
158191 export interface Info {
0 commit comments