ไลบรารีการเข้ารหัส Base64 พร้อม Arduino
Base64 เป็นระบบการเข้ารหัสที่ใช้สัญลักษณ์ 64 ตัวจัดกลุ่มเป็นข้อความที่มีความยาวทวีคูณของสี่ ข้อความเหล่านี้ (แพ็กเก็ตข้อมูล) จะเสร็จสมบูรณ์หากจำเป็น โดยมีเครื่องหมายบวก (ดังนั้นจึงใช้ 65) ซึ่งมักจะเป็นเครื่องหมายเท่ากับ (=) หากข้อมูลที่เป็นประโยชน์ที่เข้ารหัสส่งผลให้มีความยาวสั้นลง
ใช้ 64 เครื่องหมาย คุณสามารถทำงานกับตัวเลข 10 ตัวและตัวอักษรตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก (26+26) ของรหัส ASCIIปัญหาคือว่ามี 62 สัญลักษณ์ที่ไม่คลุมเครือ บวก XNUMX สัญลักษณ์ที่แตกต่างกันในการใช้งานที่แตกต่างกัน แม้ว่าบางครั้งจะเรียกด้วยสำนวนว่า "ตัวละคร" ASCII พิมพ์ได้" ในความเป็นจริงคือช่วงตั้งแต่รหัสที่แสดงด้วยรหัส 32 (เว้นวรรค) ถึง 126 (~) ส่วน 95 ที่สามารถพิมพ์ได้อย่างแท้จริง
การดำเนินการเข้ารหัส Base64 ใช้มากที่สุดนั่นคือของ พีอีเอ็มซึ่งก็ใช้โดย MIMEให้ทำงานกับเครื่องหมาย "+" และ "/" พิเศษและเครื่องหมาย "=" บนแพด เพื่อให้แพ็กเก็ตมีความยาวเป็นทวีคูณของสี่ ตัวอักษร A-Z อยู่ในตำแหน่ง 0-25 ตัวอักษร a-z อยู่ในตำแหน่ง 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 บนอุปกรณ์ที่มีทรัพยากรน้อย ดังเช่นกรณี a ไมโครคอนโทรลเลอร์ คือคุณต้องเขียนโค้ดเมื่อข้อมูลมาถึงหรือด้วยก กันชน ขั้นต่ำสุดซึ่งยังต้องมีระบบที่ระบุว่าถึงจุดสิ้นสุดของข้อความต้นฉบับแล้ว เช่น โดยการเพิ่มรหัสพิเศษ หรือโดยการใช้หมุดที่มีระดับ (ซิงโครไนซ์กับการรับ) บ่งชี้สถานะของข้อความ
โค้ดตัวอย่างด้านล่างนี้คือ a ไลบรารีสำหรับ Arduino เพื่อเข้ารหัสใน Base64 ซึ่งใช้กับทั้งสองเกณฑ์: การเข้ารหัสข้อมูลที่มาถึง (โดยไม่มี a กันชน) และรอให้สัญญาณเตือนสิ้นสุด
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
,
กำลัง 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) จำเป็นในโค้ดที่สี่เท่านั้นและเห็นแล้วว่าไม่จำเป็นต้องคำนวณ (ส่วนที่เหลือทั้งหมด) จึงไม่รวมอยู่ในนิพจน์สุดท้ายเพื่อทำให้ง่ายขึ้นแม้ว่าเราจะต้องจำไว้ ว่าประเภทของข้อมูลที่ใช้ (ไบต์) มีเพียง 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 ของตัวอย่างก่อนหน้านี้แสดงการใช้ ไลบรารีสำหรับ Arduino เพื่อเข้ารหัสใน Base64. จำเป็นต้องระบุวิธีการเท่านั้น convertir
แต่ละไบต์ที่คุณต้องการเข้ารหัสและเป็นทางเลือกไม่ว่าจะเป็นไบต์สุดท้ายหรือถ้าไม่ใช่ให้หยุดการแปลงด้วยวิธี terminar
เมื่อคุณไปถึงจุดสิ้นสุด
ดังที่เห็นในภาพหน้าจอด้านล่าง ตัวอย่างโปรแกรมของ ไลบรารีสำหรับ Arduino เพื่อเข้ารหัสใน Base64 ขั้นแรกจะแสดงข้อความที่จะเข้ารหัส Base64ในกรณีนี้เป็นจุดเริ่มต้นของเพลงดังของยักษ์ พวกลูเทียร์และต่อมาคือผลลัพธ์ของการเข้ารหัส Base64 โดยใช้รูปแบบความยาวบรรทัด MIME.
แสดงความคิดเห็น