asice

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 19 Imported by: 0

README

asice

A small, framework-agnostic Go library for assembling and inspecting ASiC-E (.asice) containers that hold XAdES signatures, per ETSI EN 319 162-1 (ASiC) and ETSI EN 319 132-1 (XAdES).

go get github.com/gmb-lib/asice

It is the reusable packaging core. By design it does no network I/O, no authentication, no HTTP, and performs no cryptographic signature verification — that SHALL BE delegated to an external validator (example: EU DSS). The only checks here are over file digests and XAdES references (hashing + XML reading), which keeps the dependency surface to the standard library.

API

Function Purpose
BuildContainer(docs, signatures, opts) ([]byte, error) Assemble a new .asice from 1..N documents + 1..N XAdES signatures.
AddSignature(container, newSignature) ([]byte, error) Add a parallel (co-) signature to an existing .asice; derives the next signatures*.xml index itself.
CoSign(original, fileless) ([]byte, error) Add the signature(s) from a fileless (hash-only) container as parallel co-signature(s) on a complete container; one-call ExtractSignatures + AddSignature.
AddDocuments(container, docs) ([]byte, error) Complete a fileless (hash-signed) container by inserting the data objects; verifies filename and digest before inserting.
ExtractSignatures(container) ([]File, error) Return the signatures*.xml entries from a container (including a fileless one); output is suitable for AddSignature.
DataObjects(container) ([]File, error) Return the container-root data objects (signed files) with their bytes; use to recompute the digests a co-signature must reference.
Inspect(container) (Manifest, []SignatureInfo, []DataObject, error) Enumerate manifest, signatures, and data objects.
CheckReferences(docs, signatures) error Verify signatures reference exactly the supplied documents (count + filename + SHA-2 digest).
Example
docs := []asice.File{
    {Name: "contract.pdf", Data: pdfBytes},
}
sigs := []asice.File{
    {Name: "xades.xml", Data: xadesBytes}, // detached XAdES from CSC / wallet
}

// Optional: validate references explicitly (BuildContainer also does this
// unless BuildOptions.SkipReferenceCheck is set).
if err := asice.CheckReferences(docs, sigs); err != nil {
    log.Fatal(err)
}

container, err := asice.BuildContainer(docs, sigs, nil)
if err != nil {
    log.Fatal(err)
}
// `container` is the raw .asice — hand it to external validation service to validate.

// Later, a second party co-signs the same data objects:
updated, err := asice.AddSignature(container, secondXadesBytes)

What it produces

A standards-shaped ASiC-E ZIP:

  • mimetypefirst entry, stored uncompressed, value application/vnd.etsi.asic-e+zip, written with a clean local header (no data descriptor) so it can be read as the container's leading bytes.
  • the original document(s) in the container root.
  • META-INF/manifest.xml — OpenDocument-style manifest; root file-entry media-type is always the ASiC-E type, then one entry per data object.
  • META-INF/signatures0.xml, signatures1.xml, … — each a XAdESSignatures wrapper around one detached ds:Signature. (No ASiCManifest.xml: for the XAdES profile the per-file references live inside the signature.)

AddSignature copies all existing entries byte-for-byte (preserving compressed bytes and CRC) and only appends a new signature file, so previously valid signatures are never disturbed. The input container is treated as immutable.

National profile: validated-by-construction

The library does not hand-tune profile specifics beyond the manifest rule above. A container is considered conformant once it passes validation through external validation service, EU DSS for example.

Errors

Mismatches return sentinel errors (use errors.Is):

ErrFileCountMismatch, ErrFilenameMismatch, ErrDigestMismatch, ErrSignatureTargetMismatch (parallel signing), ErrMalformedXAdES, ErrUnsupportedDigest, ErrInvalidContainer, ErrNoDocuments, ErrNoSignatures.

Scope / non-goals

  • No key handling, no signature creation — the signer signs externally (wallet / CSC / own tools).
  • No in-process signature cryptography — validation SHALL BE delegated to external validation service, EU DSS for example.
  • Countersignatures are out of scope (parallel/co-signatures only).

Documentation

Overview

Package asice assembles and inspects ASiC-E (.asice) containers holding XAdES signatures, per ETSI EN 319 162-1 (ASiC) and ETSI EN 319 132-1 (XAdES).

The package is deliberately framework-agnostic: it performs no network I/O, no authentication, and no HTTP. It also performs no cryptographic signature verification — that is delegated to an external validator (EU DSS). The checks here are over file digests and XAdES references only (hashing and XML reading), which keeps the dependency surface tiny.

The core entry points mirror the service design:

  • BuildContainer assembles a new .asice from documents + one or more XAdES signature files.
  • AddSignature adds a parallel (co-) signature to an existing .asice, deriving the next signature file index itself.
  • CoSign merges the signature(s) from a fileless hash-only result into an existing container as parallel co-signature(s).
  • DataObjects returns the container's data-object bytes, used to re-digest the inner files a parallel co-signature must reference.
  • Inspect enumerates the manifest, signatures, and data objects in a container.
  • CheckReferences verifies that signature files reference exactly the supplied documents (count, filename, and SHA-2 digest match).

Index

Constants

View Source
const (
	// MimeType is the ASiC-E container media type. It is the value of the
	// uncompressed "mimetype" entry that must be first in the ZIP.
	MimeType = "application/vnd.etsi.asic-e+zip"
)

Container media type and well-known container paths.

Variables

View Source
var (
	// ErrFileCountMismatch: a signature references a different number of data
	// objects than the documents supplied.
	ErrFileCountMismatch = errors.New("asice: file count mismatch")
	// ErrFilenameMismatch: a supplied filename is not referenced by the
	// signature, or vice versa.
	ErrFilenameMismatch = errors.New("asice: filename mismatch")
	// ErrDigestMismatch: a supplied file's content does not match the digest
	// recorded in the signature.
	ErrDigestMismatch = errors.New("asice: digest mismatch")
	// ErrSignatureTargetMismatch: (parallel signing) the new signature signs
	// different data objects than the container already holds.
	ErrSignatureTargetMismatch = errors.New("asice: signature targets differ from container")
	// ErrMalformedXAdES: a signature file is not parseable as XAdES / contains
	// no ds:Signature.
	ErrMalformedXAdES = errors.New("asice: malformed XAdES signature")
	// ErrUnsupportedDigest: a ds:DigestMethod algorithm is not supported.
	ErrUnsupportedDigest = errors.New("asice: unsupported digest algorithm")
	// ErrInvalidContainer: the input bytes are not a readable ASiC-E container.
	ErrInvalidContainer = errors.New("asice: invalid container")
	// ErrNoSignatures: an operation requires at least one signature but none
	// were supplied.
	ErrNoSignatures = errors.New("asice: no signatures supplied")
	// ErrNoDocuments: an operation requires at least one document but none were
	// supplied.
	ErrNoDocuments = errors.New("asice: no documents supplied")
)

Error codes reported by this package. Wrap-aware: use errors.Is against these sentinels.

Functions

func AddDocuments

func AddDocuments(container []byte, docs []File) ([]byte, error)

AddDocuments inserts data object(s) into a container that references them but is missing their bytes (e.g. a hash-signed, fileless container), producing a complete .asice. It verifies that each supplied document matches what the container's signatures reference before inserting.

func AddSignature

func AddSignature(container, newSignature []byte) ([]byte, error)

AddSignature adds a parallel (co-) signature to an existing ASiC-E container and returns a new container. The existing entries — data objects and prior signatures — are copied byte-for-byte so that previously valid signatures are never broken; the container is treated as immutable input.

The new signature must reference the same data objects the container already holds (same filenames and digests), otherwise an error wrapping ErrSignatureTargetMismatch is returned. The next signatures*.xml index is derived internally — the caller never supplies it.

func BuildContainer

func BuildContainer(docs, signatures []File, opts *BuildOptions) ([]byte, error)

BuildContainer assembles a new ASiC-E container from one or more original documents and one or more XAdES signature files, returning the raw .asice bytes.

Unless opts.SkipReferenceCheck is set, it first verifies (via CheckReferences) that the signatures reference exactly the supplied documents. The ZIP is laid out per ETSI EN 319 162-1: an uncompressed "mimetype" entry first, the documents in the container root, then META-INF/manifest.xml and one META-INF/signatures*.xml per signature.

func CheckReferences

func CheckReferences(docs, signatures []File) error

CheckReferences verifies that every supplied signature references exactly the supplied documents: same count, same filenames, and a matching digest for each document (pillar 6.1). It performs no cryptographic signature verification — only hashing and XML reading.

On mismatch it returns an error wrapping one of ErrFileCountMismatch, ErrFilenameMismatch, or ErrDigestMismatch (or ErrMalformedXAdES / ErrUnsupportedDigest while parsing).

func CoSign added in v1.3.0

func CoSign(original, fileless []byte) ([]byte, error)

CoSign adds the signature(s) carried by a fileless container — the hash-only result a signing service returns when it never held the file bytes — as parallel co-signature(s) on an existing, complete container, returning the updated container. It is the one-call form of ExtractSignatures followed by AddSignature, so the caller never has to crack the fileless result itself.

The co-signature(s) must reference exactly the data objects the original container already holds (same filenames and digests); otherwise an error wrapping ErrSignatureTargetMismatch is returned. The original's data objects and prior signatures are copied byte-for-byte, so previously valid signatures remain valid, and each added signature file is given the next free index.

func Inspect

func Inspect(container []byte) (Manifest, []SignatureInfo, []DataObject, error)

Inspect enumerates the contents of an ASiC-E container: its parsed manifest, the signatures it holds, and its data objects. It does not validate the container cryptographically.

Types

type BuildOptions

type BuildOptions struct {
	// SkipReferenceCheck disables the count/filename/digest verification that
	// BuildContainer runs before assembly. Leave false unless the caller has
	// already validated references via CheckReferences.
	SkipReferenceCheck bool
}

BuildOptions tunes container assembly. The zero value is the recommended default: reference checks run and signatures are wrapped per signature file.

type DataObject

type DataObject struct {
	Name      string
	MediaType string
	Size      int64
}

DataObject describes one container-root data object.

type File

type File struct {
	Name string
	Data []byte
}

File is a named blob: an original document or a signature file. Name is the in-container path (data objects live in the container root, so it is normally a bare filename) and Data is the raw bytes.

func DataObjects added in v1.3.0

func DataObjects(container []byte) ([]File, error)

DataObjects returns the container-root data objects (the signed files) with their bytes, in container order. The mimetype, anything under META-INF/, and directory entries are excluded. Use it to re-compute the digests a parallel co-signature must reference: a co-signature signs the existing container's inner files, not the container blob as a whole. It performs no signature verification.

It returns ErrInvalidContainer if the bytes are not a readable container, or ErrNoDocuments if the container holds no data objects (e.g. a fileless one).

func ExtractSignatures

func ExtractSignatures(container []byte) ([]File, error)

ExtractSignatures returns the signatures*.xml entries from a container, including a fileless one. The returned File.Data is suitable to pass to AddSignature.

type Manifest

type Manifest struct {
	Entries []ManifestEntry
}

Manifest is the parsed META-INF/manifest.xml (OpenDocument-style), as used by .asice profile.

type ManifestEntry

type ManifestEntry struct {
	FullPath  string // manifest:full-path ("/" for the container root entry)
	MediaType string // manifest:media-type
}

ManifestEntry is one file-entry in META-INF/manifest.xml.

type Reference

type Reference struct {
	URI         string // decoded data-object filename (container-relative)
	Algorithm   string // ds:DigestMethod Algorithm URI
	DigestValue string // ds:DigestValue, base64 as recorded in the signature
}

Reference is a single ds:Reference to an external data object, extracted from a XAdES signature's SignedInfo. Same-document references (URIs beginning with "#", e.g. the SignedProperties or KeyInfo references) are not data objects and are excluded.

type SignatureInfo

type SignatureInfo struct {
	Path       string      // e.g. META-INF/signatures0.xml
	References []Reference // data objects this signature covers
}

SignatureInfo describes one ds:Signature found in a container, with the signature file it lives in and the data objects it references.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL