import { Asn1DecodingError } from "./error.js" ; /** Enum with possible values for supported ASN.1 classes */ enum Asn1Class { Universal = 0 } /** Enum with possible values for supported ASN.1 encodings */ enum Asn1Encoding { Primitive = 0 , Constructed = 1 } /** Enum with possible values for supported ASN.1 tags */ enum Asn1Tag { BitString = 3 , ObjectIdentifier = 6 , Sequence = 16 , Null = 5 , Integer = 2 } /** Interface for ASN.1 identifiers */ interface Identifier { class : Asn1Class ; primitiveOrConstructed : Asn1Encoding ; tag : Asn1Tag ; } param identifier returns function encodeIdentifier ( identifier ) { const identifierAsNumber = ( identifier . class << 7 ) | ( identifier . primitiveOrConstructed << 5 ) | identifier . tag ; return Buffer . from ( [ identifierAsNumber ] ) ; } param length returns function encodeLength ( length ) { if ( length < 128 ) { return Buffer . from ( [ length ] ) ; } const integers = [ ] ; while ( length > 0 ) { integers . push ( length % 256 ) ; length = length >> 8 ; } integers . reverse ( ) ; return Buffer . from ( [ 128 | integers . length , ... integers ] ) ; } param buffer returns function encodeBufferAsInteger ( buffer ) { return Buffer . concat ( [ encodeIdentifier ( { class : Asn1Class . Universal , primitiveOrConstructed : Asn1Encoding . Primitive , tag : Asn1Tag . Integer , } ) , encodeLength ( buffer . length ) , buffer , ] ) ; } param oid returns function encodeObjectIdentifier ( oid ) { const oidComponents = oid . split ( "." ) . map ( ( i ) => parseInt ( i ) ) ; const firstSubidentifier = oidComponents [ 0 ] * 40 + oidComponents [ 1 ] ; const subsequentSubidentifiers = oidComponents . slice ( 2 ) . reduce ( ( expanded , component ) => { const bytes = [ ] ; do { bytes . push ( component % 128 ) ; component = component >> 7 ; } while ( component ) ; return expanded . concat ( bytes . map ( ( b , index ) => ( index ? b + 128 : b ) ) . reverse ( ) ) ; } , [ ] as number [ ] ) ; const oidBuffer = Buffer . from ( [ firstSubidentifier , ... subsequentSubidentifiers , ] ) ; return Buffer . concat ( [ encodeIdentifier ( { class : Asn1Class . Universal , primitiveOrConstructed : Asn1Encoding . Primitive , tag : Asn1Tag . ObjectIdentifier , } ) , encodeLength ( oidBuffer . length ) , oidBuffer , ] ) ; } param buffer returns function encodeBufferAsBitString ( buffer ) { const bitString = Buffer . concat ( [ Buffer . from ( [ 0 ] ) , buffer ] ) ; return Buffer . concat ( [ encodeIdentifier ( { class : Asn1Class . Universal , primitiveOrConstructed : Asn1Encoding . Primitive , tag : Asn1Tag . BitString , } ) , encodeLength ( bitString . length ) , bitString , ] ) ; } param sequenceItems returns function encodeSequence ( sequenceItems ) { const concatenated = Buffer . concat ( sequenceItems ) ; return Buffer . concat ( [ encodeIdentifier ( { class : Asn1Class . Universal , primitiveOrConstructed : Asn1Encoding . Constructed , tag : Asn1Tag . Sequence , } ) , encodeLength ( concatenated . length ) , concatenated , ] ) ; } returns function encodeNull ( ) { return Buffer . concat ( [ encodeIdentifier ( { class : Asn1Class . Universal , primitiveOrConstructed : Asn1Encoding . Primitive , tag : Asn1Tag . Null , } ) , encodeLength ( 0 ) , ] ) ; } /**
 * RSA encryption object identifier constant
 *
 * From: https://tools.ietf.org/html/rfc8017
 *
 * pkcs-1    OBJECT IDENTIFIER ::= {
 *     iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1
 * }
 *
 * -- When rsaEncryption is used in an AlgorithmIdentifier,
 * -- the parameters MUST be present and MUST be NULL.
 * --
 * rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }
 *
 * See also: http://www.oid-info.com/get/1.2.840.113549.1.1.1
 */ const ALGORITHM_RSA_ENCRYPTION = encodeSequence ( [ encodeObjectIdentifier ( "1.2.840.113549.1.1.1" ) , encodeNull ( ) , ] ) ; param n param e returns export function constructPublicKeyInDerFormat ( n , e ) { return encodeSequence ( [ ALGORITHM_RSA_ENCRYPTION , encodeBufferAsBitString ( encodeSequence ( [ encodeBufferAsInteger ( n ) , encodeBufferAsInteger ( e ) ] ) ) , ] ) ; } param identifier returns function decodeIdentifier ( identifier ) { if ( identifier >> 3 === 0b11111 ) { throw new Asn1DecodingError ( "Decoding of identifier with tag > 30 not implemented" ) ; } return { class : identifier >> 6 , primitiveOrConstructed : ( identifier >> 5 ) & 0b001 , tag : identifier & 0b11111 , } as Identifier ; } param blockOfLengthValues returns function decodeLengthValue ( blockOfLengthValues ) { if ( ! ( blockOfLengthValues [ 0 ] & 0b10000000 ) ) { return { length : blockOfLengthValues [ 0 ] , firstByteOffset : 1 , lastByteOffset : 1 + blockOfLengthValues [ 0 ] , } ; } const nrLengthOctets = blockOfLengthValues [ 0 ] & 0b01111111 ; const length = Buffer . from ( blockOfLengthValues . slice ( 1 , 1 + 1 + nrLengthOctets ) ) . readUIntBE ( 0 , nrLengthOctets ) ; return { length , firstByteOffset : 1 + nrLengthOctets , lastByteOffset : 1 + nrLengthOctets + length , } ; } /** Interface for ASN.1 identifier-length-value triplets */ interface ILV { identifier : Identifier ; length : number ; value : Buffer ; } param sequenceValue returns function decodeSequence ( sequence ) { const { tag } = decodeIdentifier ( sequence [ 0 ] ) ; if ( tag !== Asn1Tag . Sequence ) { throw new Asn1DecodingError ( `Expected a sequence to decode, but got tag ${ tag }` ) ; } const { firstByteOffset , lastByteOffset } = decodeLengthValue ( sequence . slice ( 1 ) ) ; const sequenceValue = sequence . slice ( 1 + firstByteOffset , 1 + 1 + lastByteOffset ) ; const parts = [ ] ; let offset = 0 ; while ( offset < sequenceValue . length ) { const identifier = decodeIdentifier ( sequenceValue [ offset ] ) ; const next = decodeLengthValue ( sequenceValue . slice ( offset + 1 ) ) ; const value = sequenceValue . slice ( offset + 1 + next . firstByteOffset , offset + 1 + next . lastByteOffset ) ; parts . push ( { identifier , length : next . length , value } ) ; offset += 1 + next . lastByteOffset ; } return parts ; } param bitStringValue returns function decodeBitStringWrappedSequenceValue ( bitStringValue ) { const wrappedSequence = bitStringValue . slice ( 1 ) ; return decodeSequence ( wrappedSequence ) ; } param publicKey returns export function deconstructPublicKeyInDerFormat ( publicKey ) { const [ , pubkeyinfo ] = decodeSequence ( publicKey ) ; const [ n , e ] = decodeBitStringWrappedSequenceValue ( pubkeyinfo . value ) ; return { n : n . value , e : e . value } ; }