/*
 * RSABASE - RSA encryption for Wine
 *
 * Copyright 2004 Mike McCormack for CodeWeavers
 * Copyright 2002 TransGaming Technologies
 *
 * David Hammerton
 *
 * (based upon code from dlls/wininet/netconnection.c)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */


#include "config.h"
#include "wine/port.h"

#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "wincrypt.h"

#ifdef HAVE_OPENSSL_SSL_H
#define DSA __ssl_DSA  /* avoid conflict with commctrl.h */
#undef FAR
# include <openssl/rand.h>
#undef FAR
#define FAR do_not_use_this_in_wine
#undef DSA
#endif

#include "wine/library.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(crypt);

#define RSABASE_MAGIC 0x52534100

#ifdef HAVE_OPENSSL_SSL_H

#ifndef SONAME_LIBCRYPTO
#define SONAME_LIBCRYPTO "libcrypto.so"
#endif

static void *libcrypto;

#define MAKE_FUNCPTR(f) static typeof(f) * p##f

/* OpenSSL funtions that we use */
MAKE_FUNCPTR(RAND_bytes);

static BOOL load_libcrypto( void )
{
    libcrypto = wine_dlopen(SONAME_LIBCRYPTO, RTLD_NOW, NULL, 0);
    if (!libcrypto)
    {
        MESSAGE("Couldn't load %s, RSA encryption not available.\n", SONAME_LIBCRYPTO);
        MESSAGE("Install the openssl package if you're have problems.\n");
        return FALSE;
    }

    #define GETFUNC(x) \
    p##x = wine_dlsym(libcrypto, #x, NULL, 0); \
    if (!p##x) \
    { \
        ERR("failed to load symbol %s\n", #x); \
        return FALSE; \
    }

    GETFUNC(RAND_bytes);

    return TRUE;
}

#endif

typedef struct _RSA_CryptProv
{
    DWORD dwMagic;
} RSA_CryptProv;

BOOL WINAPI RSA_CPAcquireContext(HCRYPTPROV *phProv, LPSTR pszContainer,
                   DWORD dwFlags, PVTableProvStruc pVTable)
{
    TRACE("%p %s %08lx %p\n", phProv, debugstr_a(pszContainer),
           dwFlags, pVTable);

#ifdef HAVE_OPENSSL_SSL_H

    if( !load_libcrypto() )
        return FALSE;
    else
    {
        RSA_CryptProv *cp = HeapAlloc( GetProcessHeap(), 0, sizeof (RSA_CryptProv) );
        if( !cp )
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        cp->dwMagic = RSABASE_MAGIC;

        *phProv = (HCRYPTPROV) cp;

        SetLastError(ERROR_SUCCESS);
        return TRUE;
    }
#else
    FIXME("You have to install libcrypto.so and development headers in order to use crypto API\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
#endif
}

BOOL WINAPI RSA_CPCreateHash(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash)
{
    FIXME("%08lx %d %08lx %08lx %p\n", hProv, Algid, hKey, dwFlags, phHash);
    return FALSE;
}

BOOL WINAPI RSA_CPDecrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPDeriveKey(HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPDestroyHash(HCRYPTPROV hProv, HCRYPTHASH hHash)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPDestroyKey(HCRYPTPROV hProv, HCRYPTKEY hKey)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPDuplicateHash(HCRYPTPROV hUID, HCRYPTHASH hHash, DWORD *pdwReserved, DWORD dwFlags, HCRYPTHASH *phHash)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPDuplicateKey(HCRYPTPROV hUID, HCRYPTKEY hKey, DWORD *pdwReserved, DWORD dwFlags, HCRYPTKEY *phKey)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPEncrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPExportKey(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTKEY hPubKey, DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYPTKEY *phKey)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPGenRandom(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer)
{
    BOOL ret = FALSE;
    RSA_CryptProv *cp = (RSA_CryptProv*) hProv;

    TRACE("%08lx %ld %p\n", hProv, dwLen, pbBuffer);

    if( cp && ( cp->dwMagic != RSABASE_MAGIC ) )
        return FALSE;

#ifdef HAVE_OPENSSL_SSL_H

    if( !pRAND_bytes)
        return FALSE;
    ret = pRAND_bytes( pbBuffer, dwLen );

#endif

    return ret;
}

BOOL WINAPI RSA_CPGetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPGetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPGetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPGetUserKey(HCRYPTPROV hProv, DWORD dwKeySpec, HCRYPTKEY *phUserKey)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPHashData(HCRYPTPROV hProv, HCRYPTHASH hHash, CONST BYTE *pbData, DWORD dwDataLen, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPHashSessionKey(HCRYPTPROV hProv, HCRYPTHASH hHash, HCRYPTKEY hKey, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPImportKey(HCRYPTPROV hProv, CONST BYTE *pbData, DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
{
    RSA_CryptProv *cp = (RSA_CryptProv*) hProv;

    TRACE("%08lx %08lx\n", hProv, dwFlags);

    if( cp && ( cp->dwMagic != RSABASE_MAGIC ) )
        return FALSE;

    HeapFree( GetProcessHeap(), 0, cp );

    return TRUE;
}

BOOL WINAPI RSA_CPSetHashParam(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPSetKeyParam(HCRYPTPROV hProv, HCRYPTKEY hKey, DWORD dwParam, BYTE *pbData, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPSetProvParam(HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPSignHash(HCRYPTPROV hProv, HCRYPTHASH hHash, DWORD dwKeySpec, LPCWSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen)
{
    FIXME("(stub)\n");
    return FALSE;
}

BOOL WINAPI RSA_CPVerifySignature(HCRYPTPROV hProv, HCRYPTHASH hHash, CONST BYTE *pbSignature, DWORD dwSigLen, HCRYPTKEY hPubKey, LPCWSTR sDescription, DWORD dwFlags)
{
    FIXME("(stub)\n");
    return FALSE;
}

static const WCHAR szRSAKey[] = { 'S','o','f','t','w','a','r','e','\\',
 'M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r',
 'a','p','h','y','\\','D','e','f','a','u','l','t','s','\\','P','r','o','v',
 'i','d','e','r','\\','M','i','c','r','o','s','o','f','t',' ','B','a','s',
 'e',' ','C','r','y','p','t','o','g','r','a','p','h','i','c',' ','P','r',
 'o','v','i','d','e','r',' ','v','1','.','0',0 };
static const WCHAR szRSAKey2[] = { 'S','o','f','t','w','a','r','e','\\',
 'M','i','c','r','o','s','o','f','t','\\','C','r','y','p','t','o','g','r',
 'a','p','h','y','\\','D','e','f','a','u','l','t','s','\\','P','r','o','v',
 'i','d','e','r',' ','T','y','p','e','s','\\','T','y','p','e',' ','0','0','1',0};

/***********************************************************************
 *		DllRegisterServer (RSABASE.@)
 */
HRESULT WINAPI RSABASE_DllRegisterServer()
{
    HKEY key;
    DWORD dp;
    long apiRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szRSAKey, 0, NULL,
     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &dp);

    if (apiRet == ERROR_SUCCESS)
    {
        if (dp == REG_CREATED_NEW_KEY)
        {
            static const WCHAR szImagePath[] = { 'I','m','a','g','e',' ','P','a','t','h',0 };
            static const WCHAR szRSABase[] = { 'r','s','a','b','a','s','e','.','d','l','l',0 };
            static const WCHAR szType[] = { 'T','y','p','e',0 };
            static const WCHAR szSignature[] = { 'S','i','g','n','a','t','u','r','e',0 };
            DWORD type = 1;
            DWORD sign = 0xdeadbeef;
            RegSetValueExW(key, szImagePath, 0, REG_SZ, (LPBYTE)szRSABase, (lstrlenW(szRSABase) + 1) * sizeof(WCHAR));
            RegSetValueExW(key, szType, 0, REG_DWORD, (LPBYTE)&type, sizeof(type));
            RegSetValueExW(key, szSignature, 0, REG_BINARY, (LPBYTE)&sign, sizeof(sign));
        }
        RegCloseKey(key);
    }
    if (apiRet == ERROR_SUCCESS)
        apiRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szRSAKey2, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &dp);
    if (apiRet == ERROR_SUCCESS)
    {
        if (dp == REG_CREATED_NEW_KEY)
        {
            static const WCHAR szName[] = { 'N','a','m','e',0 };
            static const WCHAR szRSAName[] = {
              'M','i','c','r','o','s','o','f','t',' ','B','a','s','e',' ','C',
              'r','y','p','t','o','g','r','a','p','h','i','c',' ','P','r',
              'o','v','i','d','e','r',' ','v','1','.','0',0 };
            static const WCHAR szTypeName[] = { 'T','y','p','e','N','a','m','e',0 };
            static const WCHAR szRSATypeName[] = {
              'R','S','A',' ','F','u','l','l',' ',
              '(','S','i','g','n','a','t','u','r','e',' ','a','n','d',' ',
              'K','e','y',' ','E','x','c','h','a','n','g','e',')',0 };

            RegSetValueExW(key, szName, 0, REG_SZ, (LPBYTE)szRSAName, sizeof(szRSAName));
            RegSetValueExW(key, szTypeName, 0, REG_SZ, (LPBYTE)szRSATypeName, sizeof(szRSATypeName));
        }
        RegCloseKey(key);
    }
    return HRESULT_FROM_WIN32(apiRet);
}

/***********************************************************************
 *		DllUnregisterServer (RSABASE.@)
 */
HRESULT WINAPI RSABASE_DllUnregisterServer()
{
    RegDeleteKeyW(HKEY_LOCAL_MACHINE, szRSAKey);
    RegDeleteKeyW(HKEY_LOCAL_MACHINE, szRSAKey2);
    return S_OK;
}
