apple/swift-asn1

How to get ASN1 Node tree of Curve25519 Public key

kmithi1 opened this issue · 1 comments

I am implementing encryption/decryption using ECDH and want to generate PEM of Public key using Curve25519. AFAIK I need to convert public key rawRepresentation data into ASN1 and then convert it to PEM.

I am struggling with creating DER from Public key and then ASN1 from DER. Please help if its possible using swift-asn1 package.

Lukasa commented

This is absolutely possible. To do it, though, requires knowing what format specifically you require. In the case of Curve25519 you probably want the Subject Public Key Info representation defined in RFC 8410 § 4.

   id-X25519    OBJECT IDENTIFIER ::= { 1 3 101 110 }
   id-Ed25519   OBJECT IDENTIFIER ::= { 1 3 101 112 }

   SubjectPublicKeyInfo  ::=  SEQUENCE  {
       algorithm         AlgorithmIdentifier,
       subjectPublicKey  BIT STRING
   }

Note that this representation uses separate AlgorithmIdentifier types for signing and key exchange, so you'll need to know what exact format you're using.

This file from our tests contains a representation of SPKI and AlgorithmIdentifier that would be suitable for this use-case. In this case RFC 8410 requires the following algorithm identifier:

The fields in AlgorithmIdentifier have the following meanings:

  • algorithm identifies the cryptographic algorithm with an object
    identifier. Four such OIDs are defined below.

  • parameters, which are optional, are the associated parameters for
    the algorithm identifier in the algorithm field.

In this document, we define four new OIDs for identifying the
different curve/algorithm pairs: the curves being curve25519 and
curve448 and the algorithms being ECDH and EdDSA in pure mode. For
all of the OIDs, the parameters MUST be absent.

Additionally, regarding the subjectPublicKey BIT STRING:

The fields in SubjectPublicKeyInfo have the following meanings:

  • algorithm is the algorithm identifier and parameters for the
    public key (see above).

  • subjectPublicKey contains the byte stream of the public key. The
    algorithms defined in this document always encode the public key
    as an exact multiple of 8 bits.

Both [RFC7748] and [RFC8032] define the public key value as being a
byte string. It should be noted that the public key is computed
differently for each of these documents; thus, the same private key
will not produce the same public key.

Putting this all together:

extension ASN1ObjectIdentifier.AlgorithmIdentifier {
    static let x25519: ASN1ObjectIdentifier = [1, 3, 101, 110]
    static let ed25519: ASN1ObjectIdentifier = [1, 3, 101, 112]
}

extension RFC5480AlgorithmIdentifier {
    static let x25519 = RFC5280AlgorithmIdentifier(algorithm: .AlgorithmIdentifier.x25519, parameters: nil)
    static let ed25519 = RFC5280AlgorithmIdentifier(algorithm: .AlgorithmIdentifier.ed25519, parameters: nil)
}

extension SubjectPublicKeyInfo {
    init(_ x25519: Curve25519.KeyAgreement.PublicKey) {
        self = SubjectPublicKeyInfo(algorithmIdentifier: .x25519, key: Array(x25519.rawRepresentation))
    }

    init(_ ed25519: Curve25519.Signing.PublicKey) {
        self = SubjectPublicKeyInfo(algorithmIdentifier: .ed25519, key: Array(ed25519.rawRepresentation))
    }
}

extension Curve25519.KeyAgreement.PublicKey {
    init?(_ spki: SubjectPublicKeyInfo) {
        guard spki.algorithmIdentifier == .AlgorithmIdentifier.x25519 else {
            return nil
        }

        self = .init(rawRepresentation: spki.key.bytes)
    }
}

extension Curve25519.Signing.PublicKey {
    init?(_ spki: SubjectPublicKeyInfo) {
        guard spki.algorithmIdentifier == .AlgorithmIdentifier.ed25519 else {
            return nil
        }

        self = .init(rawRepresentation: spki.key.bytes)
    }
}