# Migrating from libtasn1 to Synta
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Why Migrate](#why-migrate)
- [Advantages of Synta over libtasn1](#advantages-of-synta-over-libtasn1)
- [Key Differences](#key-differences)
- [API Comparison](#api-comparison)
- [Initialization](#initialization)
- [libtasn1](#libtasn1)
- [Synta](#synta)
- [Decoding](#decoding)
- [libtasn1](#libtasn1-1)
- [Synta](#synta-1)
- [Encoding](#encoding)
- [libtasn1](#libtasn1-2)
- [Synta](#synta-2)
- [Type Mapping](#type-mapping)
- [Basic Types](#basic-types)
- [Constructed Types](#constructed-types)
- [Functions](#functions)
- [Common Patterns](#common-patterns)
- [Pattern 1: Decode INTEGER](#pattern-1-decode-integer)
- [libtasn1](#libtasn1-3)
- [Synta](#synta-3)
- [Pattern 2: Decode SEQUENCE](#pattern-2-decode-sequence)
- [libtasn1](#libtasn1-4)
- [Synta](#synta-4)
- [Pattern 3: Encode SEQUENCE](#pattern-3-encode-sequence)
- [libtasn1](#libtasn1-5)
- [Synta](#synta-5)
- [Pattern 4: Optional Fields](#pattern-4-optional-fields)
- [libtasn1](#libtasn1-6)
- [Synta](#synta-6)
- [Migration Examples](#migration-examples)
- [Example 1: Parse OID](#example-1-parse-oid)
- [libtasn1](#libtasn1-7)
- [Synta](#synta-7)
- [Example 2: Iterate SEQUENCE OF](#example-2-iterate-sequence-of)
- [libtasn1](#libtasn1-8)
- [Synta](#synta-8)
- [Example 3: Create Nested Structure](#example-3-create-nested-structure)
- [libtasn1](#libtasn1-9)
- [Synta](#synta-9)
- [Safety Improvements](#safety-improvements)
- [1. No Schema Vulnerabilities](#1-no-schema-vulnerabilities)
- [2. Memory Safety](#2-memory-safety)
- [3. Type Safety](#3-type-safety)
- [4. Error Handling](#4-error-handling)
- [5. No Global State](#5-no-global-state)
- [Performance](#performance)
- [See Also](#see-also)
Guide for developers migrating from GNU libtasn1 to Synta.
## Table of Contents
- [Why Migrate](#why-migrate)
- [API Comparison](#api-comparison)
- [Type Mapping](#type-mapping)
- [Common Patterns](#common-patterns)
- [Migration Examples](#migration-examples)
- [Safety Improvements](#safety-improvements)
## Why Migrate
### Advantages of Synta over libtasn1
1. **Memory Safety**: Rust implementation eliminates entire classes of bugs
2. **No Schema Required**: Direct decoding without ASN.1 schema files
3. **Simpler API**: Fewer concepts to learn
4. **Better Type Safety**: Strongly-typed API prevents misuse
5. **Modern Error Handling**: Clear error codes vs. magic numbers
6. **Zero Dependencies**: Self-contained library
### Key Differences
| Schema | Required (ASN.1 syntax) | Not required |
| API Style | Tree-based | Streaming decoder/encoder |
| Memory Safety | Manual | Guaranteed (Rust) |
| Error Codes | Integer constants | Enum |
| Types | String-based | Strongly typed |
| Threading | Not thread-safe | Thread-safe (for separate instances) |
## API Comparison
### Initialization
#### libtasn1
```c
#include <libtasn1.h>
// Must create schema from ASN.1 definitions
asn1_node definitions = NULL;
int result = asn1_array2tree(pkix_asn1_tab, &definitions, NULL);
if (result != ASN1_SUCCESS) {
// Error
}
// Create structure from schema
asn1_node cert = NULL;
result = asn1_create_element(definitions, "PKIX1.Certificate", &cert);
```
#### Synta
```c
#include <synta.h>
// No schema needed - decode directly
SyntaDecoder *decoder = synta_decoder_new(data, len, SyntaEncoding_Der);
if (!decoder) {
// Error
}
```
### Decoding
#### libtasn1
```c
// Decode DER data into structure
result = asn1_der_decoding(&cert, der_data, der_len, NULL);
if (result != ASN1_SUCCESS) {
fprintf(stderr, "Error: %s\n", asn1_strerror(result));
}
// Read field by path
int value_len = 0;
result = asn1_read_value(cert, "tbsCertificate.serialNumber",
NULL, &value_len);
char *value = malloc(value_len);
result = asn1_read_value(cert, "tbsCertificate.serialNumber",
value, &value_len);
free(value);
asn1_delete_structure(&cert);
asn1_delete_structure(&definitions);
```
#### Synta
```c
// Decode directly
SyntaDecoder *cert_seq = NULL;
synta_decoder_enter_sequence(decoder, &cert_seq);
SyntaDecoder *tbs = NULL;
synta_decoder_enter_sequence(cert_seq, &tbs);
// Decode serial number directly
SyntaInteger *serial = NULL;
synta_decode_integer(tbs, &serial);
SyntaByteArray serial_bytes = {0};
synta_integer_to_bytes(serial, &serial_bytes);
// Use serial_bytes
synta_byte_array_free(&serial_bytes);
synta_integer_free(serial);
synta_decoder_free(tbs);
synta_decoder_free(cert_seq);
synta_decoder_free(decoder);
```
### Encoding
#### libtasn1
```c
// Create structure
asn1_node sequence = NULL;
asn1_create_element(definitions, "PKIX1.AlgorithmIdentifier", &sequence);
// Set field values
asn1_write_value(sequence, "algorithm", "1.2.840.113549.1.1.1", 1);
asn1_write_value(sequence, "parameters", NULL, 0);
// Encode to DER
int der_len = 0;
asn1_der_coding(sequence, "", NULL, &der_len, NULL);
unsigned char *der = malloc(der_len);
asn1_der_coding(sequence, "", der, &der_len, NULL);
free(der);
asn1_delete_structure(&sequence);
```
#### Synta
```c
// Encode directly
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
SyntaEncoder *seq = NULL;
synta_encoder_start_sequence(encoder, &seq);
SyntaObjectIdentifier *oid = synta_oid_from_string("1.2.840.113549.1.1.1");
synta_encode_object_identifier(seq, oid);
synta_oid_free(oid);
synta_encode_null(seq);
synta_encoder_end_constructed(seq);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
// Use output
synta_byte_array_free(&output);
```
## Type Mapping
### Basic Types
| `INTEGER` | `SyntaInteger` | Arbitrary precision |
| `BOOLEAN` | `bool` | Native C bool |
| `NULL` | N/A | No object needed |
| `OCTET STRING` | `SyntaOctetString` or `SyntaByteArray` | Byte array |
| `BIT STRING` | `SyntaByteArray` + `uint8_t` | Data + unused bits |
| `OBJECT IDENTIFIER` | `SyntaObjectIdentifier` | OID |
| `UTF8String` | `SyntaByteArray` | UTF-8 bytes |
| `PrintableString` | `SyntaByteArray` | ASCII subset |
| `IA5String` | `SyntaByteArray` | ASCII |
### Constructed Types
| `SEQUENCE` | `SyntaDecoder` (nested) | Enter/decode/exit pattern |
| `SET` | `SyntaDecoder` (nested) | Enter/decode/exit pattern |
| `CHOICE` | Manual tag check | Check tag, then decode |
| `SEQUENCE OF` | Loop with decoder | Decode elements until end |
### Functions
| `asn1_array2tree` | Not needed |
| `asn1_create_element` | `synta_decoder_new` / `synta_encoder_new` |
| `asn1_delete_structure` | `synta_decoder_free` / `synta_encoder_free` |
| `asn1_der_decoding` | `synta_decode_*` functions |
| `asn1_der_coding` | `synta_encode_*` functions |
| `asn1_read_value` | `synta_decode_*` functions |
| `asn1_write_value` | `synta_encode_*` functions |
| `asn1_strerror` | `synta_error_message` |
## Common Patterns
### Pattern 1: Decode INTEGER
#### libtasn1
```c
asn1_node integer_node = NULL;
asn1_create_element(definitions, "PKIX1.INTEGER", &integer_node);
asn1_der_decoding(&integer_node, der, der_len, NULL);
int value_len = 0;
asn1_read_value(integer_node, "", NULL, &value_len);
char *value_str = malloc(value_len);
asn1_read_value(integer_node, "", value_str, &value_len);
long value = strtol(value_str, NULL, 10);
free(value_str);
asn1_delete_structure(&integer_node);
```
#### Synta
```c
SyntaDecoder *decoder = synta_decoder_new(der, der_len, SyntaEncoding_Der);
SyntaInteger *integer = NULL;
synta_decode_integer(decoder, &integer);
int64_t value;
synta_integer_to_i64(integer, &value);
synta_integer_free(integer);
synta_decoder_free(decoder);
```
### Pattern 2: Decode SEQUENCE
#### libtasn1
```c
asn1_node sequence = NULL;
asn1_create_element(definitions, "MyModule.MySequence", &sequence);
asn1_der_decoding(&sequence, der, der_len, NULL);
// Read first element
int len1 = 0;
asn1_read_value(sequence, "field1", NULL, &len1);
char *field1 = malloc(len1);
asn1_read_value(sequence, "field1", field1, &len1);
// Read second element
int len2 = 0;
asn1_read_value(sequence, "field2", NULL, &len2);
char *field2 = malloc(len2);
asn1_read_value(sequence, "field2", field2, &len2);
free(field1);
free(field2);
asn1_delete_structure(&sequence);
```
#### Synta
```c
SyntaDecoder *decoder = synta_decoder_new(der, der_len, SyntaEncoding_Der);
SyntaDecoder *seq_decoder = NULL;
synta_decoder_enter_sequence(decoder, &seq_decoder);
// Decode first element
SyntaInteger *field1 = NULL;
synta_decode_integer(seq_decoder, &field1);
// Decode second element
SyntaByteArray field2 = {0};
synta_decode_utf8_string(seq_decoder, &field2);
synta_integer_free(field1);
synta_byte_array_free(&field2);
synta_decoder_free(seq_decoder);
synta_decoder_free(decoder);
```
### Pattern 3: Encode SEQUENCE
#### libtasn1
```c
asn1_node sequence = NULL;
asn1_create_element(definitions, "MyModule.MySequence", &sequence);
asn1_write_value(sequence, "field1", "42", 1);
asn1_write_value(sequence, "field2", "Hello", 1);
int der_len = 0;
asn1_der_coding(sequence, "", NULL, &der_len, NULL);
unsigned char *der = malloc(der_len);
asn1_der_coding(sequence, "", der, &der_len, NULL);
// Use der
free(der);
asn1_delete_structure(&sequence);
```
#### Synta
```c
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
SyntaEncoder *seq = NULL;
synta_encoder_start_sequence(encoder, &seq);
synta_encode_integer_i64(seq, 42);
synta_encode_utf8_string(seq, "Hello");
synta_encoder_end_constructed(seq);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
// Use output.data and output.len
synta_byte_array_free(&output);
```
### Pattern 4: Optional Fields
#### libtasn1
```c
int result = asn1_read_value(sequence, "optionalField", NULL, &len);
if (result == ASN1_ELEMENT_NOT_FOUND) {
// Field not present
} else if (result == ASN1_SUCCESS) {
// Field present, read it
}
```
#### Synta
```c
SyntaTag tag;
if (synta_decoder_peek_tag(decoder, &tag) == SyntaErrorCode_Success) {
if (tag.class_ == SyntaTagClass_ContextSpecific && tag.number == 0) {
// Optional [0] field present
SyntaDecoder *optional = NULL;
synta_decoder_enter_constructed(decoder, tag, &optional);
// Decode field
synta_decoder_free(optional);
}
}
```
## Migration Examples
### Example 1: Parse OID
#### libtasn1
```c
asn1_node oid_node = NULL;
asn1_create_element(definitions, "PKIX1.OBJECT_IDENTIFIER", &oid_node);
asn1_der_decoding(&oid_node, der, der_len, NULL);
int oid_len = 0;
asn1_read_value(oid_node, "", NULL, &oid_len);
char *oid_str = malloc(oid_len);
asn1_read_value(oid_node, "", oid_str, &oid_len);
printf("OID: %s\n", oid_str);
free(oid_str);
asn1_delete_structure(&oid_node);
```
#### Synta
```c
SyntaDecoder *decoder = synta_decoder_new(der, der_len, SyntaEncoding_Der);
SyntaObjectIdentifier *oid = NULL;
synta_decode_object_identifier(decoder, &oid);
char oid_str[256];
if (synta_oid_to_string(oid, oid_str, sizeof(oid_str)) > 0) {
printf("OID: %s\n", oid_str);
}
synta_oid_free(oid);
synta_decoder_free(decoder);
```
### Example 2: Iterate SEQUENCE OF
#### libtasn1
```c
asn1_node seq_of = NULL;
asn1_create_element(definitions, "MyModule.MySequenceOf", &seq_of);
asn1_der_decoding(&seq_of, der, der_len, NULL);
int i = 1;
char path[128];
while (1) {
snprintf(path, sizeof(path), "?%d", i);
int len = 0;
int result = asn1_read_value(seq_of, path, NULL, &len);
if (result == ASN1_ELEMENT_NOT_FOUND) {
break; // End of sequence
}
char *value = malloc(len);
asn1_read_value(seq_of, path, value, &len);
// Process value
free(value);
i++;
}
asn1_delete_structure(&seq_of);
```
#### Synta
```c
SyntaDecoder *decoder = synta_decoder_new(der, der_len, SyntaEncoding_Der);
SyntaDecoder *seq_decoder = NULL;
synta_decoder_enter_sequence(decoder, &seq_decoder);
while (!synta_decoder_at_end(seq_decoder)) {
SyntaInteger *value = NULL;
if (synta_decode_integer(seq_decoder, &value) == SyntaErrorCode_Success) {
// Process value
synta_integer_free(value);
}
}
synta_decoder_free(seq_decoder);
synta_decoder_free(decoder);
```
### Example 3: Create Nested Structure
#### libtasn1
```c
// Create AlgorithmIdentifier { SEQUENCE { OID, NULL } }
asn1_node alg_id = NULL;
asn1_create_element(definitions, "PKIX1.AlgorithmIdentifier", &alg_id);
asn1_write_value(alg_id, "algorithm", "1.2.840.113549.1.1.1", 1);
asn1_write_value(alg_id, "parameters", NULL, 0);
int der_len = 0;
asn1_der_coding(alg_id, "", NULL, &der_len, NULL);
unsigned char *der = malloc(der_len);
asn1_der_coding(alg_id, "", der, &der_len, NULL);
free(der);
asn1_delete_structure(&alg_id);
```
#### Synta
```c
// Create AlgorithmIdentifier { SEQUENCE { OID, NULL } }
SyntaEncoder *encoder = synta_encoder_new(SyntaEncoding_Der);
SyntaEncoder *seq = NULL;
synta_encoder_start_sequence(encoder, &seq);
SyntaObjectIdentifier *oid = synta_oid_from_string("1.2.840.113549.1.1.1");
synta_encode_object_identifier(seq, oid);
synta_oid_free(oid);
synta_encode_null(seq);
synta_encoder_end_constructed(seq);
SyntaByteArray output = {0};
synta_encoder_finish(encoder, &output);
// Use output
synta_byte_array_free(&output);
```
## Safety Improvements
### 1. No Schema Vulnerabilities
**libtasn1:**
- Schema parsing can have vulnerabilities
- Schema must match data exactly
- Malformed schema can cause crashes
**Synta:**
- No schema parsing
- Direct decoding
- Less attack surface
### 2. Memory Safety
**libtasn1 risks:**
```c
// Buffer overflow if len calculation wrong
int len = 0;
asn1_read_value(node, "field", NULL, &len);
char *buf = malloc(len); // What if len is wrong?
asn1_read_value(node, "field", buf, &len); // Overflow?
free(buf);
```
**Synta safety:**
```c
// Synta handles all buffer management safely
SyntaByteArray data = {0};
synta_decode_utf8_string(decoder, &data);
// data.len is always correct
// data.data is always valid
synta_byte_array_free(&data);
```
### 3. Type Safety
**libtasn1:**
```c
// Type confusion possible
char value[100];
int len = 100;
// What if this isn't actually an integer?
asn1_read_value(node, "field", value, &len);
long num = atol(value); // May be garbage
```
**Synta:**
```c
// Strongly typed - can't confuse types
SyntaInteger *integer = NULL;
SyntaErrorCode err = synta_decode_integer(decoder, &integer);
// Either succeeds and integer is valid INTEGER, or fails
```
### 4. Error Handling
**libtasn1:**
```c
// Easy to ignore errors
asn1_read_value(node, "field", value, &len); // Did it succeed?
```
**Synta:**
```c
// Explicit error checking encouraged
SyntaErrorCode err = synta_decode_integer(decoder, &integer);
if (err != SyntaErrorCode_Success) {
fprintf(stderr, "Error: %s\n", synta_error_message(err));
}
```
### 5. No Global State
**libtasn1:**
- Some operations use global state
- Not thread-safe
**Synta:**
- All state in decoder/encoder objects
- Thread-safe for separate instances
- Thread-local error messages
## Performance
Synta generally performs better than libtasn1 because:
1. **Zero-copy decoding**: Synta doesn't copy data unnecessarily
2. **No schema overhead**: No schema parsing or validation
3. **Efficient memory use**: Rust's ownership system minimizes allocations
4. **Better caching**: Modern compiler optimizations
## See Also
- [C API Reference](C_API.md)
- [Memory Management Guide](C_MEMORY.md)
- [Migration from OpenSSL](MIGRATION_OPENSSL.md)
- [C Examples](../synta-ffi/examples/c/)