Search

'W5500 SSL'에 해당되는 글 2건

  1. 2017.06.27 SSL/TLS embedded for IoT #8
  2. 2017.06.19 SSL/TLS embedded for IoT #7

SSL/TLS embedded for IoT #8

Embedded SSL 2017.06.27 16:34 Posted by Dwarp

SSL/TLS embedded for IoT


이 글은 embedded IoT device의 보안에 관한 글입니다.


embedded SSL/TLS 여덟번째입니다.


대만 출장으로 인해 이번 포스팅이 조금 늦었습니다. 그리고 이번 강의 후 인도 출장이 잡혀 있어 약 2-3주 간 글 업로드가 힘들 것 같습니다. 양해 바랍니다. 그럼 시작해봅시다.


저번 시간에 초기화와 handshake를 제외한 SSL 함수들을 만들어 보았습니다. 계속 이어서 가봅시다. 초기화입니다.


초기화 함수는 mbed의 example을 참고하였습니다. 해당 example은 다운로드 받은 mbedtls 소스 중 programs 폴더 -> ssl 폴더에 있습니다. 하지만 여러분들이 이 글을 읽는 이유는 embedded SSL을 구현에 시간을 단축하기 위함이므로 바로 정리해 놓은 초기화 함수를 올리겠습니다.



조금 길어요~


unsigned char wiz_mbedtls_ssl_init(wiz_ssl_context* sslContext, uint8_t* SocketHandler)
{
	int ret = 1;
#if defined (MBEDTLS_ERROR_C)
	char error_buf[100];
#endif

#if defined (MBEDTLS_DEBUG_C)
	debug_set_threshold(DEBUG_LEVEL);
#endif

	/*
	Initialize session data
	*/
#if defined (MBEDTLS_ENTROPY_C)
	sslContext->entropy = malloc(sizeof(mbedtls_entropy_context));
	mbedtls_entropy_init( sslContext->entropy);
#endif
	sslContext->ctr_drbg = malloc(sizeof(mbedtls_ctr_drbg_context));
	sslContext->ssl = malloc(sizeof(mbedtls_ssl_context));
	sslContext->conf = malloc(sizeof(mbedtls_ssl_config));
	sslContext->cacert = malloc(sizeof(mbedtls_x509_crt));

	mbedtls_ctr_drbg_init(sslContext->ctr_drbg);
	mbedtls_x509_crt_init(sslContext->cacert);
	mbedtls_ssl_init(sslContext->ssl);
	mbedtls_ssl_config_init(sslContext->conf);
	/*
	Initialize certificates
	*/
#if defined (MBEDTLS_X509_CRT_PARSE_C)
#if defined (MBEDTLS_DEBUG_C)
	printf(" Loading the CA root certificate \r\n");
#endif
	mbedtls_ssl_config_defaults((sslContext->conf),
								MBEDTLS_SSL_IS_CLIENT,
								MBEDTLS_SSL_TRANSPORT_STREAM,
								MBEDTLS_SSL_PRESET_DEFAULT);
	mbedtls_ssl_setup((sslContext->ssl), (sslContext->conf));
	//mbedtls_ssl_set_timer_cb(sslContext->ssl,NULL,SSLFSetTimerCB,SSLFGetTimerCB);
	//mbedtls_ssl_set_hostname(sslContext->ssl, HOST_NAME);
#if defined (MBEDTLS_CERTS_C)
	ret = mbedtls_x509_crt_parse((sslContext->cacert),(unsigned char *)CERTIFICATE,strlen(CERTIFICATE));
#else
	ret = 1;
#if defined (MBEDTLS_DEBUG_C)
	printf("SSL_CERTS_C not define .\r\n");
#endif
#endif
#endif
	if(ret < 0)
	{
#if defined (MBEDTLS_CERTS_C)
		printf("x509_crt_parse failed.%x \r\n",ret);
#endif
		return 0;
	}
	/*
	set ssl session para
	*/
	mbedtls_ssl_conf_ca_chain(sslContext->conf, sslContext->cacert, NULL);
	mbedtls_ssl_conf_endpoint(sslContext->conf,MBEDTLS_SSL_IS_CLIENT); 		//set the current communication method is SSL Client
	mbedtls_ssl_conf_authmode(sslContext->conf,MBEDTLS_SSL_VERIFY_REQUIRED);
	mbedtls_ssl_conf_rng(sslContext->conf,SSLRandomCB,sslContext->ctr_drbg);
        mbedtls_ssl_conf_read_timeout(sslContext->conf,WIZ_RECV_TIMEOUT_VALUE);
#if defined (MBEDTLS_DEBUG_C)
	mbedtls_ssl_conf_dbg(sslContext->conf,wiz_mbedtls_ssl_debug_cb,stdout);
#endif
	mbedtls_ssl_set_bio(sslContext->ssl,SocketHandler, wiz_mbedtls_ssl_send, wiz_mbedtls_ssl_recv, wiz_mbedtls_ssl_recvtimeout); //set client's socket send and receive functions

	return 1;
}

코드를 간단하게 설명하겠습니다.

함수 자체는 wiz_ssl_context* sslContext와 uint8_t* sockethandler인자를 받습니다. 여기서 sslContext는 ssl에 필요한 context 변수들을 structure로 선언해 놓은 것이고 sockethandler는 단순하게 소켓 번호라고 생각하시면 됩니다.

아래는 wiz_ssl_context의 structure definition 입니다.

typedef struct{
#if defined (MBEDTLS_ENTROPY_C)
	mbedtls_entropy_context* entropy;
#endif
	mbedtls_ctr_drbg_context* ctr_drbg;
	mbedtls_ssl_context* ssl;
	mbedtls_ssl_config* conf;
	mbedtls_x509_crt* cacert;
}wiz_ssl_context;

structure 내부에 여러가지 변수들이 있는데 각각의 의미는 별도의 블로깅이 필요합니다. 일단 우리는 embedded ssl library를 사용하는 것에 목적을 두고 블로깅을 하고 있으니 너무 심오하게 생각하지 말고 넘어갑시다.


이 후에는 각각의 변수를 malloc을 통해 memory를 할당합니다. embedded에서 왠 malloc? 이라고 생각하시는 분들이 많을 수도 있습니다. 그냥 쓰세요. 어차피 mbedtls 자체가 malloc을 사용하고 있으니까요. 그래도 malloc이 불편하신 분들은 global로 선언해서 사용하셔도 되는데 대신 유연성이 엄청 떨어지겠죠? 트레이드 오프가 필요합니다.(malloc을 사용하기 때문에 최소 48kb보다 큰 sram을 가지고 있는 mcu를 추천하는 것입니다.)


그리고 각각을 mbedtls의 내장 함수들로 초기화 합니다. 그 다음은 ssl configuration을 설정하구요. 여기서 client로 할지 TCP로 할지 결정을 합니다. 그 이후에는 mbedtls_ssl_setup함수를 통해 sslcontext와 configuration을 setup 하구요.


이 후 Certificate를 등록합니다. 여기에서 CERTIFICATE에 주목할 필요가 있는데요. 이 CERTIFICATE는 Server의 ROOT certificate를 등록해야 합니다. 해당 certificate는 시스템에 내장해야 하는데요. 내장하지 않으면 내가 접속하려는 server가 진짜인지 가짜인지 구분을 할 수 없습니다. 한마디로 피싱 사이트를 구분해내지 못한다고 생각하시면 됩니다.

ret = mbedtls_x509_crt_parse((sslContext->cacert),(unsigned char *)CERTIFICATE,strlen(CERTIFICATE)); 

해당 Certificate는 인터넷 브라우져에서 다운로드 받아 text로 저장한 뒤 certificate.h 라는 파일을 하나 만들어서 복붙하시고 다음과 같은 형식으로 수정하시면 됩니다. (Certificate를 코드에 적용하는 방)

const char	graph_facebook_com[] = \
"-----BEGIN CERTIFICATE-----\r\n"  \
"MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs\r\n"  \
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\n"  \
"d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\r\n"  \
"ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL\r\n"  \
"MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3\r\n"  \
"LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy\r\n"  \
"YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2\r\n"  \
"4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC\r\n"  \
"Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1\r\n"  \
"itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn\r\n"  \
"4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X\r\n"  \
"sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft\r\n"  \
"bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA\r\n"  \
"MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw\r\n"  \
"NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy\r\n"  \
"dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t\r\n"  \
"L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG\r\n"  \
"BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ\r\n"  \
"UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D\r\n"  \
"aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd\r\n"  \
"aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH\r\n"  \
"E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly\r\n"  \
"/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu\r\n"  \
"xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF\r\n"  \
"0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae\r\n"  \
"cPUeybQ=\r\n"  \
"-----END CERTIFICATE-----\r\n";

이제 session 파라미터들을 입력하고 앞서 만들어 놓은 random, recv, send 등의 콜백 함수를 등록하면 마무리 됩니다.


웁스! 한가지 말씀 안드린게 있어요. Debug용 call back도 등록하셔야 하는데 mbedtls에서 제공하는 형식만 맞추고 함수 내부에서 uart와 연결된 printf를 사용하시면 됩니다.

#if defined (MBEDTLS_DEBUG_C)
void wiz_mbedtls_ssl_debug_cb(void *ctx, int level, const char *file, int line, const char *str)
{
    if(level < DEBUG_LEVEL)
    {
       printf("%s\r\n",str);
    }
}
#endif

그럼 이것으로 초기화 함수에 대한 설명은 마무리 짓고 handshake로 넘어가겠습니다. SSL의 첫번째 과정이 바로 Handshake입니다. 구글링 해보시면 ssl handshake에 대해 많이 나오는데 딱히 이해못할 만한 내용은 없습니다. handshake 과정 중에서 암호화 방식을 결정하고 certificate 교환하는 게 전부입니다. handshake가 끝나면 암호화된 데이터로 통신이 가능하게 됩니다.


unsigned int wiz_mbedtls_ssl_handshake(wiz_ssl_context* sslContext)
{
    int ret;
    uint32_t flags;
//#if defined(MBEDTLS_ERROR_C)
//    unsigned char error_buf[100];
//    memset(error_buf, 0, 100);
//#endif
#if defined(WIZINTERFACE_DEBUG)
    printf( "  . Performing the SSL/TLS handshake..." );
#endif

    while( ( ret = mbedtls_ssl_handshake( sslContext->ssl ) ) != 0 )
    {
        if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
        {
//#if defined(MBEDTLS_ERROR_C)
//            mbedtls_strerror( ret, (char *) error_buf, 100 );
//            printf( " failed\n\r  ! mbedtls_ssl_handshake returned %d: %s\n\r", ret, error_buf );
//#endif
            return( -1 );
        }
    }
#if defined(WIZINTERFACE_DEBUG)
    printf( " ok\n\r    [ Ciphersuite is %s ]\n\r", mbedtls_ssl_get_ciphersuite( sslContext->ssl ) );
    printf( "  . Verifying peer X.509 certificate..." );
#endif
    /* In real life, we probably want to bail out when ret != 0 */
    if( ( flags = mbedtls_ssl_get_verify_result( sslContext->ssl ) ) != 0 )
    {
        //char vrfy_buf[512];
#if defined(WIZINTERFACE_DEBUG)
        printf( " failed.\n\r" );
#endif
        //mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), "  ! ", flags );

        //printf( "%s\n\r", vrfy_buf );
    }
    else
        printf( " ok.\n\r" );

    return( 0 );
}


handshake 함수는 생각보다 짧습니다. 위 코드를 보면 mbedtls_ssl_handshake로 handshake가 완료될 때까지 뺑뺑이를 돌린 후 certificate가 맞는지 확인만 하면 됩니다. 생각보다 간단하지요? 그럼 어느정도 ssl을 위한 함수가 준비되었습니다.


이제 실전!!! 실전!!! 실전!!! 음... SSL로 어디에 접속해보면 좋을까 생각해 올게요.


혹시 의견 있으시면 댓글 달아 주세요. 반영해 드립니다. ^^ 그럼 수고하시구요. 전 인도 벵갈루루에 다녀와야할 것 같아요.ㅠㅠ 인도는 덥고 지금 우기이고 ㅠㅠ 암튼 7월 10일 이후에나 다시 포스팅을 할 수 있을 것 같습니다. 다음에 봐요~ 안녕~


(일부 코드 내용이 틀린 점이 있습니다. 코드가 모두 완성되면 zip file로 업로드 할테니까 너무 조바심 내지 마시구요~ ^^)



저작자 표시
신고

'Embedded SSL' 카테고리의 다른 글

SSL/TLS embedded for IoT #8  (0) 2017.06.27
SSL/TLS embedded for IoT #7  (0) 2017.06.19
SSL/TLS embedded for IoT #6  (0) 2017.06.17
SSL/TLS embedded for IoT #5  (0) 2017.06.16
SSL/TLS embedded for IoT #4  (0) 2017.06.15
SSL/TLS embedded for IoT #3  (6) 2016.12.28

SSL/TLS embedded for IoT #7

Embedded SSL 2017.06.19 17:00 Posted by Dwarp

SSL/TLS embedded for IoT


이 글은 embedded IoT device의 보안에 관한 글입니다.


embedded SSL/TLS 일곱번째입니다.



다시 달려 보겠습니다.(뭐 물론 저 혼자만의 느낌이지만.. ㅠㅠ)


저번시간에 만들어야할 함수 목록들이 있었습니다. 7개 정도? 있었지요? 한 번 만들어 봅시다. 물론 mbedTLS configuration 등 신경써야 할 부분이 많습니다. 하지만 장담하건데 시작하시기도 전에 지치실 수 있습니다. 그러니 아무 생각 말고 걱정 말고 따라 오셔요 ㅎㅎ.


아래가 만들어야 할 함수 목록 이라고 말씀드렸습니다. 사실 안만드셔도 됩니다. 하지만 사용하시려면 결국 만드실 겁니다. 


wiz_mbedtls_ssl_init();

wiz_mbedtls_ssl_deinit();

wiz_mbedtls_ssl_handshake();

wiz_mbedtls_ssl_random();

wiz_mbedtls_ssl_send();

wiz_mbedtls_ssl_recv();

wiz_mbedtls_ssl_recvtimeout();


자 그럼 뭐부터 할까요? 전 언제나 가장 쉬운 것부터 합니다. 가장 기본적인 send와 recv 함수부터 만들어 보도록 하겠습니다. 이 함수는 매우 간단합니다. recv와 send를 할 수 있는 network interface를 정의하는 부분이어서 mbedTLS 형식에 맞게 ioLibrary를 포팅하면 됩니다. 코드를 먼저 보시는게 편할 것 같습니다. 아래가 해당 코드입니다. 기존의 ioLibrary에서 제공한 send함수와 recv 함수를 이용해 mbedTLS가 사용하는 형식이 맞추어 한번 싸준 것 뿐입니다.

int wiz_mbedtls_ssl_send(void *ctx, const unsigned char *buf, unsigned int len )
{
	while(getSn_TX_FSR(*((int *)ctx)) < len && len < getSn_TxMAX(*((int *)ctx))){};
	return send(*((int *)ctx),(uint8_t*)buf,len);
}

int wiz_mbedtls_ssl_recv(void *ctx, unsigned char *buf, unsigned int len )
{
      return (recv(*((int *)ctx),buf,len));
}

여기서 ctx는 read,write callback이 공유하는 parameter(context)라고 설명되어 있는데 그냥 socket 번호라고 생각하시면 됩니다. 자 벌써 2개 끝났습니다. 그럼 recvtimeout도 해볼까요? 뭐 어렵겠어요? recv와 똑같지만 timeout이 발생하면 error를 return하면 됩니다. 아래는 코드입니다.

int WIZnetRecvTimeOut(void *ctx, unsigned char *buf, unsigned int len, unsigned int timeout)
{
	uint32_t startTick = HAL_GetTick();
	unsigned int ret;
	do
	{
		if(getSn_RX_RSR(*((int *)ctx))){
			return recv(*((int *)ctx),buf,len);
		}
	}while((HAL_GetTick() - startTick) <= timeout);
	return MBEDTLS_ERR_SSL_TIMEOUT;
}

어때요? recv와 기본적으로 동일하지만 timeout 값을 인자로 하나 더 받아 HAL_GetTick()에 의해 timeout이 발생하면 MBEDTLS_ERR_SSL_TIMEOUT 이라는 에러를 반환합니다. 엄청 간단하죠? 이 역시 mbedTLS가 요구하는 형식입니다.


여기까지는 크게 어려움이 없는 것 같습니다. 그러면 그 다음으로 간단한 random()을 보겠습니다. 요기서부터는 약간 생각해 볼만한 여지가 있습니다. mbedTLS의 callback으로 등록해야 하는 함수의 원형을 보면 다음과 같습니다.


void mbedtls_ssl_conf_rng( mbedtls_ssl_config *conf, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng );

여기에서 f_rng에 함수포인터로 등록을 시켜줘야 하는데 해당 함수를 또 찾아가면 p_rng를 f_rng의 첫번째 인자로 사용합니다. mbedTLS의 예제를 보면 더욱 혼란스러워집니다. ㅎㅎㅎ drbg라는 context를 넣는데 해당 변수는 AES 기반의 random을 만들어내는 context로 보이거든요. 그래서 다시 오기를 부려 찾아봤습니다. 그럼 f_rng가 등록이 안되어 있으면 p_rng를 사용할까요? 그건 또 아니었습니다. f_rng를 등록하지 않으면 에러는 아니지만 MBEDTLS_ERR_SSL_NO_RNG 이라는 error를 리턴합니다. 그리고 p_rng는 random의 seed도 아닙니다. 그냥 변수의 포인터이므로 seed로 사용하시면 안됩니다.!!!!

어쨌든 결론은 f_rng를 반드시 등록해야만 한다! 입니다. p_rng의 용도는 아직 저도 더 연구가 필요한 것 같습니다. 아무튼 용로를 잘 모르겠으니 무시하고 rand 함수를 감싸도록 하겠습니다.

int wiz_mbedtls_ssl_random( void *p_rng, unsigned char *output, size_t output_len )
{
    int i;

	if(output_len <= 0)
	{
         return (1);
	}
    for(i = 0;i < output_len;i++)
    {
       *output++ = rand() % 0xff;
	}
    srand(rand());
	return (0);
}


그냥 간단하게 코딩했습니다. 하지만 이렇게 하면 안된다는 걸 여러분들은 알고 있지요~~~?????네 그렇습니다. 원칙적으로는 true random을 넣어야 하는데 그렇지 않으면 random의 페턴이 확인 가능합니다. 그러면 보안성이 떨이지겠죠? 그래서 MCU Pin 하나를 open시킨 상태로 Analog input으로 설정한 후 rand() 함수의 seed로 활용하는 분들도 계십니다. Pin을 open시킨 상태로 Analog 값을 측정하면 값이 계속 흔들리거든요.(사실 이 방법이 random에 완벽한 대응이 되는지는 저도 잘 모르겠습니다. 이론적으로 더 확실한 방법을 찾으면 별도로 포스팅하겠습니다.)


휴~ 그림 한장없이 진행하려니까 힘드네요.


<우워워워워워어 한가인!!!! 사랑해요 한가인! ㅠㅠ 유부녀 히잉>


한가인 사진을 보내 설레여서 더 이상 못 쓰겠습니다.


그리고 사실 초기화와 핸드쉐이크 부분은 조금 까다롭기 때문에 다음 시간에 이어서 하겠습니다. 오늘은 여기까지만 하시구요. 여러분들은 보는데 10분이지만 저는 작성하는데 반나절씩 걸립니다. ㅠㅠ 히잉


암튼 다음 강의도 너무 많이 기다리시지 않게 되도록 빨리 올리도록 하겠습니다.


월요일이네요 한주 화이팅 하세요.

저작자 표시
신고

'Embedded SSL' 카테고리의 다른 글

SSL/TLS embedded for IoT #8  (0) 2017.06.27
SSL/TLS embedded for IoT #7  (0) 2017.06.19
SSL/TLS embedded for IoT #6  (0) 2017.06.17
SSL/TLS embedded for IoT #5  (0) 2017.06.16
SSL/TLS embedded for IoT #4  (0) 2017.06.15
SSL/TLS embedded for IoT #3  (6) 2016.12.28