Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F18824766
der-encoder.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
14 KB
Subscribers
None
der-encoder.c
View Options
/* der-decoder.c - Distinguished Encoding Rules Encoder
* Copyright (C) 2001, 2004, 2008, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* KSBA 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 General Public
* License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see <http://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<assert.h>
#include
"util.h"
#include
"ksba.h"
#include
"asn1-func.h"
#include
"ber-help.h"
#include
"der-encoder.h"
#include
"convert.h"
struct
der_encoder_s
{
AsnNode
module
;
/* the ASN.1 structure */
ksba_writer_t
writer
;
const
char
*
last_errdesc
;
/* string with the error description */
AsnNode
root
;
/* of the expanded parse tree */
int
debug
;
};
/* To be useful for the DER encoder we store all data direct as the
binary image, so we use the VALTYPE_MEM */
static
gpg_error_t
store_value
(
AsnNode
node
,
const
void
*
buffer
,
size_t
length
)
{
_ksba_asn_set_value
(
node
,
VALTYPE_MEM
,
buffer
,
length
);
return
0
;
}
static
void
clear_value
(
AsnNode
node
)
{
_ksba_asn_set_value
(
node
,
VALTYPE_NULL
,
NULL
,
0
);
}
DerEncoder
_ksba_der_encoder_new
(
void
)
{
DerEncoder
d
;
d
=
xtrycalloc
(
1
,
sizeof
*
d
);
if
(
!
d
)
return
NULL
;
return
d
;
}
void
_ksba_der_encoder_release
(
DerEncoder
d
)
{
xfree
(
d
);
}
/**
* _ksba_der_encoder_set_module:
* @d: Decoder object
* @module: ASN.1 Parse tree
*
* Initialize the decoder with the ASN.1 module. Note, that this is a
* shallow copy of the module. Fixme: What about ref-counting of
* AsnNodes?
*
* Return value: 0 on success or an error code
**/
gpg_error_t
_ksba_der_encoder_set_module
(
DerEncoder
d
,
ksba_asn_tree_t
module
)
{
if
(
!
d
||
!
module
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
if
(
d
->
module
)
return
gpg_error
(
GPG_ERR_CONFLICT
);
/* module already set */
d
->
module
=
module
->
parse_tree
;
return
0
;
}
gpg_error_t
_ksba_der_encoder_set_writer
(
DerEncoder
d
,
ksba_writer_t
w
)
{
if
(
!
d
||
!
w
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
if
(
d
->
writer
)
return
gpg_error
(
GPG_ERR_CONFLICT
);
/* reader already set */
d
->
writer
=
w
;
return
0
;
}
/*
Helpers to construct and write out objects
*/
/* Create and write a
AlgorithmIdentifier ::= SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL
}
where parameters will be set to NULL if parm is NULL or to an octet
string with the given parm. As a special hack parameter will not be
written if PARM is given but parmlen is 0. */
gpg_error_t
_ksba_der_write_algorithm_identifier
(
ksba_writer_t
w
,
const
char
*
oid
,
const
void
*
parm
,
size_t
parmlen
)
{
gpg_error_t
err
;
unsigned
char
*
buf
;
size_t
len
;
int
no_null
=
(
parm
&&
!
parmlen
);
err
=
ksba_oid_from_str
(
oid
,
&
buf
,
&
len
);
if
(
err
)
return
err
;
/* write the sequence */
/* fixme: the the length to encode the TLV values are actually not
just 2 byte each but depend on the length of the values - for
our purposes the static values do work. */
err
=
_ksba_ber_write_tl
(
w
,
TYPE_SEQUENCE
,
CLASS_UNIVERSAL
,
1
,
(
no_null
?
2
:
4
)
+
len
+
(
parm
?
parmlen
:
0
));
if
(
err
)
goto
leave
;
/* the OBJECT ID header and the value */
err
=
_ksba_ber_write_tl
(
w
,
TYPE_OBJECT_ID
,
CLASS_UNIVERSAL
,
0
,
len
);
if
(
!
err
)
err
=
ksba_writer_write
(
w
,
buf
,
len
);
if
(
err
)
goto
leave
;
/* Write the parameter */
if
(
no_null
)
;
else
if
(
parm
)
{
err
=
_ksba_ber_write_tl
(
w
,
TYPE_OCTET_STRING
,
CLASS_UNIVERSAL
,
0
,
parmlen
);
if
(
!
err
)
err
=
ksba_writer_write
(
w
,
parm
,
parmlen
);
}
else
{
err
=
_ksba_ber_write_tl
(
w
,
TYPE_NULL
,
CLASS_UNIVERSAL
,
0
,
0
);
}
leave
:
xfree
(
buf
);
return
err
;
}
/*************************************************
*** Copy data from a tree image to the tree ***
*************************************************/
/* Copy all values from the tree SRC (with values store in SRCIMAGE)
to the tree DST */
gpg_error_t
_ksba_der_copy_tree
(
AsnNode
dst_root
,
AsnNode
src_root
,
const
unsigned
char
*
src_image
)
{
AsnNode
s
,
d
;
s
=
src_root
;
d
=
dst_root
;
/* note: we use the is_any flags becuase an inserted copy may have
already changed the any tag to the actual type */
while
(
s
&&
d
&&
(
s
->
type
==
d
->
type
||
d
->
flags
.
is_any
))
{
if
(
d
->
flags
.
is_any
)
d
->
type
=
s
->
type
;
if
(
s
->
flags
.
in_array
&&
s
->
right
)
{
if
(
!
_ksba_asn_insert_copy
(
d
))
return
gpg_error
(
GPG_ERR_ENOMEM
);
}
if
(
!
_ksba_asn_is_primitive
(
s
->
type
)
)
;
else
if
(
s
->
off
==
-1
)
clear_value
(
d
);
else
store_value
(
d
,
src_image
+
s
->
off
+
s
->
nhdr
,
s
->
len
);
s
=
_ksba_asn_walk_tree
(
src_root
,
s
);
d
=
_ksba_asn_walk_tree
(
dst_root
,
d
);
}
if
(
s
||
d
)
{
/* fputs ("ksba_der_copy_tree: trees don't match\nSOURCE TREE:\n", stderr); */
/* _ksba_asn_node_dump_all (src_root, stderr); */
/* fputs ("DESTINATION TREE:\n", stderr); */
/* _ksba_asn_node_dump_all (dst_root, stderr); */
return
gpg_error
(
GPG_ERR_ENCODING_PROBLEM
);
}
return
0
;
}
/*********************************************
********** Store data in a tree *************
*********************************************/
gpg_error_t
_ksba_der_store_time
(
AsnNode
node
,
const
ksba_isotime_t
atime
)
{
char
buf
[
50
],
*
p
;
int
need_gen
;
gpg_error_t
err
;
/* First check that ATIME is indeed as formatted as expected. */
err
=
_ksba_assert_time_format
(
atime
);
if
(
err
)
return
err
;
memcpy
(
buf
,
atime
,
8
);
memcpy
(
buf
+
8
,
atime
+
9
,
6
);
strcpy
(
buf
+
14
,
"Z"
);
/* We need to use generalized time beginning with the year 2050. */
need_gen
=
(
_ksba_cmp_time
(
atime
,
"20500101T000000"
)
>=
0
);
if
(
node
->
type
==
TYPE_ANY
)
node
->
type
=
need_gen
?
TYPE_GENERALIZED_TIME
:
TYPE_UTC_TIME
;
else
if
(
node
->
type
==
TYPE_CHOICE
)
{
/* find a suitable choice to store the value */
AsnNode
n
;
for
(
n
=
node
->
down
;
n
;
n
=
n
->
right
)
{
if
(
(
need_gen
&&
n
->
type
==
TYPE_GENERALIZED_TIME
)
||
(
!
need_gen
&&
n
->
type
==
TYPE_UTC_TIME
))
{
node
=
n
;
break
;
}
}
}
if
(
node
->
type
==
TYPE_GENERALIZED_TIME
||
node
->
type
==
TYPE_UTC_TIME
)
{
p
=
node
->
type
==
TYPE_UTC_TIME
?
(
buf
+
2
)
:
buf
;
return
store_value
(
node
,
p
,
strlen
(
p
));
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
/* Store the utf-8 STRING in NODE. */
gpg_error_t
_ksba_der_store_string
(
AsnNode
node
,
const
char
*
string
)
{
if
(
node
->
type
==
TYPE_CHOICE
)
{
/* find a suitable choice to store the value */
}
if
(
node
->
type
==
TYPE_PRINTABLE_STRING
)
{
return
store_value
(
node
,
string
,
strlen
(
string
));
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
/* Store the integer VALUE in NODE. VALUE is assumed to be a DER
encoded integer prefixed with 4 bytes given its length in network
byte order. */
gpg_error_t
_ksba_der_store_integer
(
AsnNode
node
,
const
unsigned
char
*
value
)
{
if
(
node
->
type
==
TYPE_INTEGER
)
{
size_t
len
;
len
=
(
value
[
0
]
<<
24
)
|
(
value
[
1
]
<<
16
)
|
(
value
[
2
]
<<
8
)
|
value
[
3
];
return
store_value
(
node
,
value
+
4
,
len
);
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
gpg_error_t
_ksba_der_store_oid
(
AsnNode
node
,
const
char
*
oid
)
{
gpg_error_t
err
;
if
(
node
->
type
==
TYPE_ANY
)
node
->
type
=
TYPE_OBJECT_ID
;
if
(
node
->
type
==
TYPE_OBJECT_ID
)
{
unsigned
char
*
buf
;
size_t
len
;
err
=
ksba_oid_from_str
(
oid
,
&
buf
,
&
len
);
if
(
err
)
return
err
;
err
=
store_value
(
node
,
buf
,
len
);
xfree
(
buf
);
return
err
;
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
gpg_error_t
_ksba_der_store_octet_string
(
AsnNode
node
,
const
char
*
buf
,
size_t
len
)
{
if
(
node
->
type
==
TYPE_ANY
)
node
->
type
=
TYPE_OCTET_STRING
;
if
(
node
->
type
==
TYPE_OCTET_STRING
)
{
return
store_value
(
node
,
buf
,
len
);
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
gpg_error_t
_ksba_der_store_sequence
(
AsnNode
node
,
const
unsigned
char
*
buf
,
size_t
len
)
{
if
(
node
->
type
==
TYPE_ANY
)
node
->
type
=
TYPE_PRE_SEQUENCE
;
if
(
node
->
type
==
TYPE_SEQUENCE
||
node
->
type
==
TYPE_PRE_SEQUENCE
)
{
return
store_value
(
node
,
buf
,
len
);
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
gpg_error_t
_ksba_der_store_null
(
AsnNode
node
)
{
if
(
node
->
type
==
TYPE_ANY
)
node
->
type
=
TYPE_NULL
;
if
(
node
->
type
==
TYPE_NULL
)
{
clear_value
(
node
);
return
0
;
}
else
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
/*
Actual DER encoder
*/
/* We have a value for this node. Calculate the length of the header
and store it in node->nhdr and store the length of the value in
node->value. We assume that this is a primitive node and has a
value of type VALTYPE_MEM. */
static
void
set_nhdr_and_len
(
AsnNode
node
,
unsigned
long
length
)
{
int
buflen
=
0
;
if
(
node
->
type
==
TYPE_SET_OF
||
node
->
type
==
TYPE_SEQUENCE_OF
)
buflen
++
;
else
if
(
node
->
type
==
TYPE_TAG
)
buflen
++
;
else
if
(
node
->
type
<
0x1f
||
node
->
type
==
TYPE_PRE_SEQUENCE
)
buflen
++
;
else
{
never_reached
();
/* Fixme: tags with values above 31 are not yet implemented */
}
if
(
!
node
->
type
/*&& !class*/
)
buflen
++
;
/* end tag */
else
if
(
node
->
type
==
TYPE_NULL
/*&& !class*/
)
buflen
++
;
/* NULL tag */
else
if
(
!
length
)
buflen
++
;
/* indefinite length */
else
if
(
length
<
128
)
buflen
++
;
else
{
buflen
+=
(
length
<=
0xff
?
2
:
length
<=
0xffff
?
3
:
length
<=
0xffffff
?
4
:
5
);
}
node
->
len
=
length
;
node
->
nhdr
=
buflen
;
}
/* Like above but put now put it into buffer. return the number of
bytes copied. There is no need to do length checking here */
static
size_t
copy_nhdr_and_len
(
unsigned
char
*
buffer
,
AsnNode
node
)
{
unsigned
char
*
p
=
buffer
;
int
tag
,
class
;
unsigned
long
length
;
tag
=
node
->
type
;
class
=
CLASS_UNIVERSAL
;
length
=
node
->
len
;
if
(
tag
==
TYPE_SET_OF
)
tag
=
TYPE_SET
;
else
if
(
tag
==
TYPE_SEQUENCE_OF
)
tag
=
TYPE_SEQUENCE
;
else
if
(
tag
==
TYPE_PRE_SEQUENCE
)
tag
=
TYPE_SEQUENCE
;
else
if
(
tag
==
TYPE_TAG
)
{
class
=
CLASS_CONTEXT
;
/* Hmmm: we no way to handle other classes */
tag
=
node
->
value
.
v_ulong
;
}
if
(
tag
<
0x1f
)
{
*
p
=
(
class
<<
6
)
|
tag
;
if
(
!
_ksba_asn_is_primitive
(
tag
))
*
p
|=
0x20
;
p
++
;
}
else
{
/* fixme: Not_Implemented*/
}
if
(
!
tag
&&
!
class
)
*
p
++
=
0
;
/* end tag */
else
if
(
tag
==
TYPE_NULL
&&
!
class
)
*
p
++
=
0
;
/* NULL tag */
else
if
(
!
length
)
*
p
++
=
0x80
;
/* indefinite length - can't happen! */
else
if
(
length
<
128
)
*
p
++
=
length
;
else
{
int
i
;
/* fixme: if we know the sizeof an ulong we could support larger
objects - however this is pretty ridiculous */
i
=
(
length
<=
0xff
?
1
:
length
<=
0xffff
?
2
:
length
<=
0xffffff
?
3
:
4
);
*
p
++
=
(
0x80
|
i
);
if
(
i
>
3
)
*
p
++
=
length
>>
24
;
if
(
i
>
2
)
*
p
++
=
length
>>
16
;
if
(
i
>
1
)
*
p
++
=
length
>>
8
;
*
p
++
=
length
;
}
return
p
-
buffer
;
}
static
unsigned
long
sum_up_lengths
(
AsnNode
root
)
{
AsnNode
n
;
unsigned
long
len
=
0
;
if
(
root
->
type
==
TYPE_NULL
)
return
root
->
nhdr
;
if
(
!
(
n
=
root
->
down
)
||
_ksba_asn_is_primitive
(
root
->
type
))
len
=
root
->
len
;
else
{
for
(;
n
;
n
=
n
->
right
)
len
+=
sum_up_lengths
(
n
);
}
if
(
!
_ksba_asn_is_primitive
(
root
->
type
)
&&
root
->
type
!=
TYPE_CHOICE
&&
len
&&
!
root
->
flags
.
is_implicit
)
{
/* this is a constructed one */
set_nhdr_and_len
(
root
,
len
);
}
return
len
?
(
len
+
root
->
nhdr
)
:
0
;
}
/* Create a DER encoding from the value tree ROOT and return an
allocated image of appropriate length in r_image and r_imagelen.
The value tree is modified so that it can be used the same way as a
parsed one, i.e the elements off, and len are set to point into
image. */
gpg_error_t
_ksba_der_encode_tree
(
AsnNode
root
,
unsigned
char
**
r_image
,
size_t
*
r_imagelen
)
{
AsnNode
n
;
unsigned
char
*
image
;
size_t
imagelen
,
len
;
/* clear out all fields */
for
(
n
=
root
;
n
;
n
=
_ksba_asn_walk_tree
(
root
,
n
))
{
n
->
off
=
-1
;
n
->
len
=
0
;
n
->
nhdr
=
0
;
}
/* Set default values */
/* FIXME */
/* calculate the length of the headers. These are the tag and
length fields of all primitive elements */
for
(
n
=
root
;
n
;
n
=
_ksba_asn_walk_tree
(
root
,
n
))
{
if
(
_ksba_asn_is_primitive
(
n
->
type
)
&&
!
n
->
flags
.
is_implicit
&&
((
n
->
valuetype
==
VALTYPE_MEM
&&
n
->
value
.
v_mem
.
len
)
||
n
->
type
==
TYPE_NULL
))
set_nhdr_and_len
(
n
,
n
->
value
.
v_mem
.
len
);
}
/* Now calculate the length of all constructed types */
imagelen
=
sum_up_lengths
(
root
);
#if 0
/* set off to zero, so that it can be dumped */
for (n=root; n ; n = _ksba_asn_walk_tree (root, n))
n->off = 0;
fputs ("DER encoded value Tree:\n", stderr);
_ksba_asn_node_dump_all (root, stderr);
for (n=root; n ; n = _ksba_asn_walk_tree (root, n))
n->off = -1;
#endif
/* now we can create an encoding in image */
image
=
xtrymalloc
(
imagelen
);
if
(
!
image
)
return
gpg_error
(
GPG_ERR_ENOMEM
);
len
=
0
;
for
(
n
=
root
;
n
;
n
=
_ksba_asn_walk_tree
(
root
,
n
))
{
size_t
nbytes
;
if
(
!
n
->
nhdr
)
continue
;
assert
(
n
->
off
==
-1
);
assert
(
len
<
imagelen
);
n
->
off
=
len
;
nbytes
=
copy_nhdr_and_len
(
image
+
len
,
n
);
len
+=
nbytes
;
if
(
_ksba_asn_is_primitive
(
n
->
type
)
&&
n
->
valuetype
==
VALTYPE_MEM
&&
n
->
value
.
v_mem
.
len
)
{
nbytes
=
n
->
value
.
v_mem
.
len
;
assert
(
len
+
nbytes
<=
imagelen
);
memcpy
(
image
+
len
,
n
->
value
.
v_mem
.
buf
,
nbytes
);
len
+=
nbytes
;
}
}
assert
(
len
==
imagelen
);
*
r_image
=
image
;
if
(
r_imagelen
)
*
r_imagelen
=
imagelen
;
return
0
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Dec 23, 1:20 PM (54 m, 46 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a5/67/706d8501d5d991d7a7939f7edb74
Attached To
rK libksba
Event Timeline
Log In to Comment