PeculiarVentures / asn1-schema

asn1-schema is a collection of TypeScript schemas that make working with common ASN.1 objects easy
32 stars 11 forks source link

Creating PKCS8 `PrivateKeyInfo` seems to be missing `0x04 0x20` #82

Open CMCDragonkai opened 1 year ago

CMCDragonkai commented 1 year ago

I'm trying to use asn1-pkcs8 to construct a PrivateKeyInfo for Ed25519 private key.

I noticed that during construction the serialized buffer looks different from the one that is produced by @peculiar/webcrypto. It turns out that it's missing 0x04 0x20 prefix on the key.

import * as asn1 from '@peculiar/asn1-schema';
import * as asn1Pkcs8 from '@peculiar/asn1-pkcs8';
import * as asn1X509 from '@peculiar/asn1-x509';
import * as x509 from '@peculiar/x509';

  const algorithm = new asn1X509.AlgorithmIdentifier({
    algorithm: x509.idEd25519
  });

  // Notice I had to put `0x04 0x20` here manually
  const key = new asn1Pkcs8.PrivateKey(
    Buffer.concat([
      Buffer.from([0x04, 0x20]),
      privateKey
    ])
  );

  const pkcs8 = new asn1Pkcs8.PrivateKeyInfo({
    privateKeyAlgorithm: algorithm,
    privateKey: key,
  });

  // This should be 48 bytes, if I don't add `0x04 0x20` above, it ends up being 46 bytes
  const data = utils.bufferWrap(asn1.AsnSerializer.serialize(pkcs8));

If I don't concatenate 0x04 0x20, the resulting key size is 46 bytes.

But if I use webcrypto, I get 48 bytes.

The interface for PrivateKeyInfo requires PrivateKey which extends OctetString.

I'm trying to reconcile the difference here, it seems webcrypto is more correct here. So I'm wondering if something was missing about how PrivateKeyInfo should be constructed.

I noticed that PrivateKeyInfo in webcrypto-core is not the same as the one in asn1-pkcs8.

microshine commented 1 year ago

I've updated your example. You should convert your privateKey variable into the OctetString class from @peculiar/asn1-schema package.

PrivateKeyInfo.PrivateKey keeps private key data. It's OCTET STRING for ED25519, but SEQUENCE for RSA. So you should create and serialize the correct data for the PrivateKey field

const crypto = require('crypto').webcrypto
const asn1 = require('@peculiar/asn1-schema');
const asn1Pkcs8 = require('@peculiar/asn1-pkcs8');
const asn1X509 = require('@peculiar/asn1-x509');

const algorithm = new asn1X509.AlgorithmIdentifier({
  algorithm: '1.3.101.112',
});

// ED private key data
const raw = crypto.getRandomValues(new Uint8Array(32))

// CurvePrivateKey ::= OCTET STRING
const curvePrivateKey = new asn1.OctetString(raw.byteLength);
// copy raw
const edPrivateKeyVew = new Uint8Array(curvePrivateKey.buffer);
edPrivateKeyVew.set(raw);

const pkcs8 = new asn1Pkcs8.PrivateKeyInfo({
  privateKeyAlgorithm: algorithm,
  privateKey: new asn1Pkcs8.PrivateKey(asn1.AsnConvert.serialize(curvePrivateKey)),
});

console.log(asn1.AsnConvert.toString(pkcs8));
console.log();
console.log("HEX:", Buffer.from(asn1.AsnConvert.serialize(pkcs8)).toString("hex"));

Console

SEQUENCE :
  INTEGER : 0
  SEQUENCE :
    OBJECT IDENTIFIER : 1.3.101.112
  OCTET STRING :
    OCTET STRING : 5d50746f08d85888ab2ec91d0bd14b92a96e24e5d90f7d7265a33ac9c1d08413

HEX: 302e020100300506032b6570042204205d50746f08d85888ab2ec91d0bd14b92a96e24e5d90f7d7265a33ac9c1d08413