File: src\libraries\Common\src\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
 
using Microsoft.Win32.SafeHandles;
 
internal static partial class Interop
{
    internal static partial class Crypto
    {
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ObjTxt2Obj", StringMarshalling = StringMarshalling.Utf8)]
        internal static partial SafeAsn1ObjectHandle ObjTxt2Obj(string s);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ObjObj2Txt")]
        private static unsafe partial int ObjObj2Txt(byte* buf, int buf_len, IntPtr a);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetObjectDefinitionByName", StringMarshalling = StringMarshalling.Utf8)]
        private static partial IntPtr CryptoNative_GetObjectDefinitionByName(string friendlyName);
        internal static IntPtr GetObjectDefinitionByName(string friendlyName)
        {
            IntPtr ret = CryptoNative_GetObjectDefinitionByName(friendlyName);
            if (ret == IntPtr.Zero)
            {
                ErrClearError();
            }
 
            return ret;
        }
 
        // Returns shared pointers, should not be tracked as a SafeHandle.
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_ObjNid2Obj")]
        internal static partial IntPtr ObjNid2Obj(int nid);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Asn1ObjectFree")]
        internal static partial void Asn1ObjectFree(IntPtr o);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Asn1OctetStringNew")]
        internal static partial SafeAsn1OctetStringHandle Asn1OctetStringNew();
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Asn1OctetStringSet")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static partial bool Asn1OctetStringSet(SafeAsn1OctetStringHandle o, byte[] d, int len);
 
        [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Asn1OctetStringFree")]
        internal static partial void Asn1OctetStringFree(IntPtr o);
 
        internal static unsafe string GetOidValue(IntPtr asn1ObjectPtr)
        {
            // OBJ_obj2txt returns the number of bytes that should have been in the answer, but it does not accept
            // a NULL buffer.  The documentation says "A buffer length of 80 should be more than enough to handle
            // any OID encountered in practice", so start with a buffer of size 80, and try again if required.
            const int StackCapacity = 80;
            byte* bufStack = stackalloc byte[StackCapacity];
 
            int bytesNeeded = ObjObj2Txt(bufStack, StackCapacity, asn1ObjectPtr);
 
            if (bytesNeeded < 0)
            {
                throw CreateOpenSslCryptographicException();
            }
 
            Debug.Assert(bytesNeeded != 0, "OBJ_obj2txt reported a zero-length response");
 
            if (bytesNeeded < StackCapacity)
            {
                return Marshal.PtrToStringUTF8((IntPtr)bufStack, bytesNeeded);
            }
 
            // bytesNeeded does not count the \0 which will be written on the end (based on OpenSSL 1.0.1f),
            // so make sure to leave room for it.
            int initialBytesNeeded = bytesNeeded;
            byte[] bufHeap = new byte[bytesNeeded + 1];
            fixed (byte* buf = &bufHeap[0])
            {
                bytesNeeded = ObjObj2Txt(buf, bufHeap.Length, asn1ObjectPtr);
 
                if (bytesNeeded < 0)
                {
                    throw CreateOpenSslCryptographicException();
                }
 
                Debug.Assert(
                    bytesNeeded == initialBytesNeeded,
                    "OBJ_obj2txt changed the required number of bytes for the realloc call");
 
                if (bytesNeeded > initialBytesNeeded)
                {
                    // OBJ_obj2txt is demanding yet more memory
                    throw new CryptographicException();
                }
 
                return Marshal.PtrToStringUTF8((IntPtr)buf, bytesNeeded);
            }
        }
    }
}