مكتبة ترميز Base64 مع الاردوينو
Base64 هو نظام ترميز يستخدم 64 رمزًا مجمعة في رسائل طولها مضاعف لأربعة. يتم إكمال هذه الرسائل (حزم البيانات)، إذا لزم الأمر، برمز زائد (لذا يتم استخدام 65)، وغالبًا ما تكون علامة التساوي (=)، إذا كانت المعلومات المفيدة المشفرة تؤدي إلى طول أقصر.
باستخدام 64 علامة، يمكنك العمل مع الأرقام العشرة والأحرف الكبيرة والصغيرة (10+26) من الكود ASCII، المشكلة هي أن هناك 62 رمزًا لا لبس فيه، بالإضافة إلى رمزين يختلفان في التطبيقات المختلفة. على الرغم من الإشارة إليها أحيانًا بتعبير "الشخصيات". ASCII قابلة للطباعة"، في الواقع هي تلك التي تتراوح من تلك التي يمثلها الكود 32 (مسافة) إلى 126 (~) 95 القابلة للطباعة حقًا.
تنفيذ الترميز Base64 الأكثر استخداما، أن من بيم، والذي يستخدمه أيضًا MIME، استخدم علامتي "+" و"/" الإضافيتين وعلامة "=" على اللوحة بحيث يكون طول الحزم مضاعفًا لأربعة. تشغل الحروف AZ المواضع 0-25، والحروف az تشغل المواضع 26-51، والأرقام 0-9 تشغل المواضع 52-61، وعلامة الجمع (+) المواضع 62، والموضع 63 تشغله الشرطة المائلة (/ ).
طريقة تمثيل البيانات بالتنسيق Base64 يتكون من أخذ مجموعات من البيانات الأصلية 6 بت والتي يتم تمثيلها بالرمز المقابل. إذا كانت هناك بتات متبقية، فسيتم ملؤها بالأصفار إلى اليمين. إذا لم يكن عدد الرموز الناتج من مضاعفات الأربعة، فسيتم ملؤه بعلامات المساواة إلى اليمين.
الصورة التالية توضح الترميز ASCII للنص ("أوم") وطريقة تحويله إليه Base64. نظرًا لوجود 7 رموز، يجب ملء الرسالة النهائية بعلامة يساوي في النهاية. ويمكن القول أن النص "أوم" في ASCII يعادل «b2htaW8=" في Base64.
استخدامات محددة للترميز Base64 كما أنها تفرض عادة الحد الأقصى لطول الخط. التطبيق MIME يحد كل سطر من 76 حرفًا. عادةً ما يتم فصل الأسطر برمز نهاية السطر (CR، ممثلاً بالقيمة 0x0D في ASCII) وسطر جديد آخر (NL، الذي يتوافق مع الكود ASCII 0x0A).
الإزعاج الذي يضاف عند تنفيذ الترميز Base64 على جهاز به موارد قليلة، كما هو الحال غالبًا مع ملف متحكم هو أنه يجب عليك الترميز عند وصول المعلومات أو باستخدام ملف العازلة الحد الأدنى، والذي يتطلب أيضًا توفير نظام يشير إلى الوصول إلى نهاية الرسالة الأصلية، على سبيل المثال، عن طريق إضافة رمز خاص، أو باستخدام دبوس يشير مستواه (متزامن مع الاستقبال) إلى حالة الرسالة.
رمز المثال أدناه هو أ مكتبة لاردوينو للتشفير في Base64 والذي يتم تنفيذه باستخدام كلا المعيارين: تشفير المعلومات التي تصل (بدون ملف العازلة) وانتظر حتى تنتهي إشارة التحذير.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//base64.h
#include <string.h> // memcpy/strncpy
#define LONGITUD_LINEA 76
#define MASCARA_B64 0B00111111
#define ULTIMO_CODIGO_BASE64 64 // 64 caracteres más el signo igual (empezando a contar desde cero)
#define MAXIMA_LONGITUD_RESULTADO 6 // Máximo número de caracteres del resultado parcial de la codificación. Puede ser 1 si no se ha llegado al final de un bloque (2 bytes en el original, 4 en la conversión), 2 si se ha llegado al final de un bloque, 3 si no se ha llegado al final de un bloque pero se supera la longitud máxima de la línea, 4 si se llega al final de un bloque y se supera la longitud máxima de la línea, 5 si hay que rellenar con un signo igual o 6 si hay que rellenar con dos signos igual
class Base64
{
private:
unsigned char simbolo_base64[ULTIMO_CODIGO_BASE64+1]; // Espacio para la codificación Base64, el relleno (=) una terminación en \0
unsigned int numero_valor; // Posición (empezando en cero) que ocupa el valor que se desea convertir en el mensaje completo original
unsigned int numero_codigo; // Posición del último código calculado. Podría limitarse al ancho de la línea (LONGITUD_LINEA, 76 caracteres) pero usando un contenedor alto se podría implementar también una cuenta estadística
unsigned char resto_base64; // Último resto obtenido al calcular el último código
unsigned char resultado[MAXIMA_LONGITUD_RESULTADO+1]; // Resultado de la conversión actual. Si es terminal puede incluir el caracter 65 (=) una o dos veces
unsigned char contador_caracteres_resultado=0;
void acumular_resultado(unsigned char valor);
public:
Base64();
~Base64();
void iniciar_conversion();
unsigned char *convertir(unsigned char valor_original, bool terminar_conversion);
unsigned char *convertir(unsigned char valor_original);
unsigned char *terminar();
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
//base64.cpp
#include “base64.h”
Base64::Base64() // Constructor
{
memcpy(simbolo_base64,“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”,ULTIMO_CODIGO_BASE64+1);
iniciar_conversion();
}
Base64::~Base64() // Destructor
{
}
void Base64::iniciar_conversion()
{
numero_valor=0;
numero_codigo=0;
resto_base64=0;
}
unsigned char *Base64::convertir(unsigned char valor_original, bool terminar_conversion) // Valor que se desea convertir a Base64
{
convertir(valor_original);
if(terminar_conversion)
{
terminar();
}
return resultado;
}
unsigned char *Base64::convertir(unsigned char valor_original) // Valor que se desea convertir a Base64
{
unsigned char desplazamiento;
contador_caracteres_resultado=0;
acumular_resultado((valor_original>>(2+(numero_valor%3)*2))|resto_base64);
desplazamiento=4–(numero_valor%3)*2;
resto_base64=(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento;
if(((numero_codigo+1)%4==0))
{
acumular_resultado(resto_base64);
resto_base64=0;
}
numero_valor++;
resultado[contador_caracteres_resultado]=0;
return resultado;
}
unsigned char *Base64::terminar()
{
if(numero_codigo%4)
{
acumular_resultado(resto_base64);
while(numero_codigo%4)
{
acumular_resultado(ULTIMO_CODIGO_BASE64);
}
}
resultado[contador_caracteres_resultado]=0;
iniciar_conversion();
return resultado;
}
void Base64::acumular_resultado(unsigned char valor)
{
numero_codigo++;
resultado[contador_caracteres_resultado++]=simbolo_base64[valor];
if((numero_codigo%LONGITUD_LINEA)==0)
{
resultado[contador_caracteres_resultado++]=13; // CR “\r”
resultado[contador_caracteres_resultado++]=10; // LF “\n”
}
}
|
الجزء الأساسي من حساب الكود Base64 يتم ذلك بالتعبير:
(valor_original>>(2+(numero_valor%3)*2))|resto_base64
وحساب الباقي بالتعبير:
(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento
,
siendo desplazamiento
القيمة التي يتم حسابها بالتعبير:
4-(numero_valor%3)*2
تتكون العملية المتبعة للحصول على هذه التعبيرات من تعميم حساب كل من الرموز الأربعة Base64 الناتجة عن تمثيل ثلاث بايتات من القيمة الأصلية.
Base64=((byte_1>>2)|resto)&0b00111111 |
resto=(byte_1&0b00000011)<<4 |
Base64=((byte_2>>4)|resto)&0b00111111 |
resto=(byte_2&0b00001111)<<2 |
Base64=((byte_3>>6)|resto)&0b00111111 |
resto=(byte_3&0b00111111)<<0 |
Base64=((byte_3>>0)|resto)&0b00111111 |
resto=(byte_3&0b00111111)<<0 |
مع النص Base64
يشير الكود الكاذب أعلاه إلى الكود الموجود في Base64 الذي يتم حسابه. لقد تم استخدام التعبير byte_n
للإشارة إلى البايت n الذي يتم ترميزه. النص resto
يمثل البتات المتبقية من البايت الذي يتم تشفيره. في بداية الحساب يفترض أن الباقي هو صفر
من أجل الوضوح، في الكود الكاذب السابق، تم تضمين قناع 6 بت في حساب جميع الرموز، على الرغم من أنه من الضروري فقط تحديد آخرها، حيث يتم تدوير الآخرين بحيث يتم فقدان معظم البتات دائمًا. بارِز.
كما هو واضح، الكود الرابع هو كل الباقي وليس هناك حاجة لحساب الباقي بعد ذلك؛ ولذلك فمن الضروري فقط تنفيذ ثلاث خطوات، واحدة لكل بايت مشفر. من المهم أن نتذكر أنه إذا لم يتم تشفير البايت الثالث في الحزمة، فيجب ملء الكود الأخير بالأصفار على اليمين. Base64 تم الحصول عليها.
للتعميم، التدوير الصحيح للتعبير الذي يحسب الكود فيه Base64 يمكن تمثيلها على أنها 2+(numero_byte%3)*2
بحيث يدور الجزء الموجود داخل القوسين من صفر إلى اثنين، فينتج عن ذلك 2 و4 و6 في كل خطوة. بالطبع ليست هذه هي الطريقة الوحيدة للتعميم، لكنني اخترت هذه الطريقة من أجل الوظيفة وقبل كل شيء من أجل الوضوح. بما أن القناع (و) كان ضروريًا فقط في الكود الرابع وقد رأينا بالفعل أنه ليس من الضروري حسابه (كله الباقي)، لم يتم تضمينه في التعبير النهائي لتبسيطه، على الرغم من أننا يجب أن نتذكر أن نوع البيانات المستخدمة (بايت) يتم أخذ البتات الستة الأقل أهمية فقط.
يمكن تعميم الدوران الأيسر للباقي بطريقة مشابهة للطريقة السابقة. ويمكن أيضًا ملاحظة أن القناع المطبق (AND) يخضع لنفس دوران البتات ولكن في الاتجاه المعاكس. هذا هو السبب لحساب النزوح مع 4-(numero_valor%3)*2
قبل تطبيقه بالمعنى المناسب لكل جزء من أجزاء التعبير.
يوضح المثال التالي كيفية استخدام المكتبة لترميز سلسلة نصية (تذكر ذلك Base64 يمكن استخدامها لأي مجموعة بيانات، مثل الصورة، على سبيل المثال). يوجد في الكود التالي بعض التفاصيل المثيرة للاهتمام للتوضيح. أولاً، تم استخدام رمز خاص (الرمز ~) للإشارة إلى نهاية النص، بدلاً من إشارة الأجهزة أو الإشارة إلى طول النص. ومن الناحية المنطقية، لا يمكن أن يكون هذا الرمز جزءًا من البيانات التي تم ترميزها.
أما المسألة الثانية التي يجب أخذها بعين الاعتبار، وهي على قدر أهميتها الواضحة، فهي أن وحدة فك التشفير في الوجهة يجب أن تعرف كيفية تمثيل المعلومات التي تصل إليها. يتضمن النص أحرفًا لا تنتمي إلى المجموعة ASCII قابلة للطباعة (من 32 إلى 126)، الحروف ذات اللكنة، على سبيل المثال. اردوينو سيستخدم بايتين (UTF-8) لتمثيل هذه الشخصيات. لا يمكن ببساطة استخدام المعتاد \0
كفاصل نص، لأنه في كثير من الحالات، يكون البايت الأول الذي يتم تمثيل الحرف به صفرًا بالضبط.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include “base64.h”
char texto_prueba[]=“La bella y graciosa moza marchose a lavar la ropa.\nLa mojó en el arroyuelo y cantando la lavó.\nLa frotó sobre una piedra, la colgó de un abedul.\nDespués de lavar la ropa, la niña se fue al mercado.\nUn pastor vendía ovejas pregonando a viva voz:\nved qué oveja, ved qué lana, ved qué bestia, qué animal.\nLa niña la vio muy flaca, sin embargo le gustó.\nYo te pago veinte escudos y no discutamos más.\nVuelve la niña cantando muy contenta con su oveja.\nCuando llegaron al bosque la ovejita se escapó.\nLa niña desesperada arrojose encima de ella.\nVelozmente y con destreza aferrola por detrás.\nLlegaba por el camino jinete de altivo porte.\nDescendió de su caballo y a la niña le cantó…~”;
char *resultado;
Base64 base64;
void setup()
{
Serial.begin(9600);
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) // ¿Es un Arduino Leonardo (ATmega32U4)?
while(!Serial){}; // Esperar a Arduino Leonardo
#endif
// Mostrar el texto original
unsigned int contador=0;
while(texto_prueba[contador]!=‘~’)
{
//Serial.println(String(texto_prueba[contador])+”=”+String(texto_prueba[contador],DEC));
Serial.print(String(texto_prueba[contador]));
contador++;
}
Serial.println(“\n”);
// Mostrar el texto codificado en Base64
contador=0;
while(texto_prueba[contador]!=‘~’)
{
resultado=base64.convertir(texto_prueba[contador],texto_prueba[contador+1]==‘~’);
byte contador_resultado=0;
while(resultado[contador_resultado]>0)
{
Serial.print(String(resultado[contador_resultado]));
contador_resultado++;
}
contador++;
}
}
void loop()
{
}
|
يوضح السطر 26 من المثال السابق استخدام مكتبة لاردوينو للتشفير في Base64. من الضروري فقط الإشارة إلى الطريقة convertir
كل بايت تريد تشفيره واختياريًا ما إذا كان هو الأخير أو، إذا لم يكن الأمر كذلك، قم بإيقاف التحويل باستخدام الطريقة terminar
عندما تصل إلى النهاية.
كما هو موضح في لقطة الشاشة أدناه، فإن البرنامج النموذجي لـ مكتبة لاردوينو للتشفير في Base64 يعرض أولاً النص الذي سيتم ترميزه Base64وفي هذه الحالة بداية أغنية العمالقة الشهيرة Luthiers، وبعد ذلك نتيجة الترميز في Base64 باستخدام طول سطر التنسيق MIME.
أكتب تعليق