Skip to content

Commit

Permalink
Extend XAdES LTA signatures
Browse files Browse the repository at this point in the history
IB-7995

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Oct 4, 2024
1 parent 85f7c27 commit 6c5fa6b
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 109 deletions.
5 changes: 5 additions & 0 deletions examples/DigiDocCSharp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ private static void Verify(string file)
Console.WriteLine("Time: " + s.trustedSigningTime());
Console.WriteLine("Cert: " + s.signingCertificate().Subject);
Console.WriteLine("TimeStamp: " + s.TimeStampCertificate().Subject);
foreach (TSAInfo tsaInfo in s.ArchiveTimeStamps())
{
Console.WriteLine("Archive Time: " + tsaInfo.time);
Console.WriteLine("Archive Cert: " + tsaInfo.cert.Subject);
}

s.validate();
Console.WriteLine("Signature is valid");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ static void verify(String file) {
System.out.println("Time: " + signature.trustedSigningTime());
System.out.println("Cert: " + signature.signingCertificate().getSubjectDN().toString());
System.out.println("TimeStamp Cert: " + signature.TimeStampCertificate().getSubjectDN().toString());
for(TSAInfo tsaInfo : signature.ArchiveTimeStamps()) {
System.out.println("Archive Time: " + tsaInfo.getTime());
System.out.println("Archive Cert: " + tsaInfo.getCert().getSubjectDN().toString());
}

try
{
Expand Down
31 changes: 26 additions & 5 deletions libdigidocpp.i
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,20 @@ extern "C"
$result = jenv->NewByteArray(temp.size());
jenv->SetByteArrayRegion($result, 0, temp.size(), (const jbyte*)temp.data());
%}
%typemap(jtype) std::vector<unsigned char>, digidoc::X509Cert "byte[]"
%typemap(out) digidoc::X509Cert * %{
std::vector<unsigned char> temp = *$1;
$result = jenv->NewByteArray(temp.size());
jenv->SetByteArrayRegion($result, 0, temp.size(), (const jbyte*)temp.data());
%}
%typemap(jtype) std::vector<unsigned char>, digidoc::X509Cert, digidoc::X509Cert * "byte[]"
%typemap(jstype) std::vector<unsigned char> "byte[]"
%typemap(jstype) digidoc::X509Cert "java.security.cert.X509Certificate"
%typemap(jni) std::vector<unsigned char>, digidoc::X509Cert "jbyteArray"
%typemap(jstype) digidoc::X509Cert, digidoc::X509Cert* "java.security.cert.X509Certificate"
%typemap(jni) std::vector<unsigned char>, digidoc::X509Cert, digidoc::X509Cert * "jbyteArray"
%typemap(javain) std::vector<unsigned char>, digidoc::X509Cert "$javainput"
%typemap(javaout) std::vector<unsigned char> {
return $jnicall;
}
%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert {
%typemap(javaout, throws="java.security.cert.CertificateException, java.io.IOException") digidoc::X509Cert, digidoc::X509Cert * {
byte[] der = $jnicall;
java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X509");
try (java.io.ByteArrayInputStream is = new java.io.ByteArrayInputStream(der)) {
Expand All @@ -117,7 +122,7 @@ extern "C"

#elif defined(SWIGCSHARP)
%typemap(cstype) std::vector<unsigned char> "byte[]"
%typemap(cstype) digidoc::X509Cert "System.Security.Cryptography.X509Certificates.X509Certificate2"
%typemap(cstype) digidoc::X509Cert, digidoc::X509Cert* "System.Security.Cryptography.X509Certificates.X509Certificate2"
%typemap(csin, pre= " global::System.IntPtr cPtr$csinput = digidocPINVOKE.ByteVector_to($csinput, $csinput.Length);
var handleRef$csinput = new global::System.Runtime.InteropServices.HandleRef(this, cPtr$csinput);"
) std::vector<unsigned char> "handleRef$csinput"
Expand All @@ -137,6 +142,14 @@ extern "C"
$modulePINVOKE.ByteVector_free(cPtr);
return new System.Security.Cryptography.X509Certificates.X509Certificate2(der);
}
%typemap(csvarout, excode=SWIGEXCODE2) digidoc::X509Cert * %{
get {
global::System.IntPtr cPtr = $imcall;$excode
byte[] der = new byte[$modulePINVOKE.ByteVector_size(cPtr)];
global::System.Runtime.InteropServices.Marshal.Copy($modulePINVOKE.ByteVector_data(cPtr), der, 0, der.Length);
$modulePINVOKE.ByteVector_free(cPtr);
return new System.Security.Cryptography.X509Certificates.X509Certificate2(der);
} %}
%typemap(out) std::vector<unsigned char> %{ $result = new std::vector<unsigned char>(std::move($1)); %}
%typemap(out) digidoc::X509Cert %{ $result = new std::vector<unsigned char>($1); %}

Expand All @@ -161,6 +174,10 @@ extern "C"
std::vector<unsigned char> temp = $1;
$result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size());
}
%typemap(out) digidoc::X509Cert * {
std::vector<unsigned char> temp = *$1;
$result = PyBytes_FromStringAndSize((const char*)temp.data(), temp.size());
}
#endif
%apply std::vector<unsigned char> { std::vector<unsigned char> const & };

Expand Down Expand Up @@ -210,6 +227,9 @@ extern "C"
%newobject digidoc::Container::open;
%newobject digidoc::Container::create;

%immutable digidoc::TSAInfo::cert;
%immutable digidoc::TSAInfo::time;

%feature("director") digidoc::ContainerOpenCB;

%typemap(javacode) digidoc::Conf %{
Expand Down Expand Up @@ -267,6 +287,7 @@ def transfer(self):
%template(StringMap) std::map<std::string,std::string>;
%template(DataFiles) std::vector<digidoc::DataFile*>;
%template(Signatures) std::vector<digidoc::Signature*>;
%template(TSAInfos) std::vector<digidoc::TSAInfo>;

%extend digidoc::Container {
static digidoc::Container* open(const std::string &path, digidoc::ContainerOpenCB *cb)
Expand Down
5 changes: 5 additions & 0 deletions src/Signature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ X509Cert Signature::ArchiveTimeStampCertificate() const { return X509Cert(); }
*/
string Signature::ArchiveTimeStampTime() const { return {}; }

/**
* Returns signature Archive TimeStampTokens.
*/
vector<TSAInfo> Signature::ArchiveTimeStamps() const { return {}; }

struct Signature::Validator::Private
{
Status result = Valid;
Expand Down
22 changes: 15 additions & 7 deletions src/Signature.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@

#include "Exception.h"

#include <string>
#include <vector>
#include "crypto/X509Cert.h"

namespace digidoc
{
class X509Cert;

struct TSAInfo {
X509Cert cert;
std::string time;
};

class DIGIDOCPP_EXPORT Signature
{
public:
Expand Down Expand Up @@ -85,18 +90,18 @@ namespace digidoc
virtual std::string countryName() const;
virtual std::vector<std::string> signerRoles() const;

//TM profile properties
// TM profile properties
virtual std::string OCSPProducedAt() const;
virtual X509Cert OCSPCertificate() const;
DIGIDOCPP_DEPRECATED virtual std::vector<unsigned char> OCSPNonce() const;

//TS profile properties
// TS profile properties
virtual X509Cert TimeStampCertificate() const;
virtual std::string TimeStampTime() const;

//TSA profile properties
virtual X509Cert ArchiveTimeStampCertificate() const;
virtual std::string ArchiveTimeStampTime() const;
// TSA profile properties
DIGIDOCPP_DEPRECATED virtual X509Cert ArchiveTimeStampCertificate() const;
DIGIDOCPP_DEPRECATED virtual std::string ArchiveTimeStampTime() const;

// Xades properties
virtual std::string streetAddress() const;
Expand All @@ -110,6 +115,9 @@ namespace digidoc
// Other
virtual std::vector<unsigned char> messageImprint() const;

//TSA profile properties
virtual std::vector<TSAInfo> ArchiveTimeStamps() const;

protected:
Signature();

Expand Down
3 changes: 3 additions & 0 deletions src/SignatureXAdES_B.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ SignatureXAdES_B::SignatureXAdES_B(const std::shared_ptr<Signatures> &signatures
"AttrAuthoritiesCertValues", "AttributeRevocationValues", "ArchiveTimeStamp"})
if(usp/elem)
THROW("%s is not supported", elem);
for(const char *elem: {"CompleteCertificateRefsV2", "AttributeCertificateRefsV2", "SigAndRefsTimeStampV2", "RefsOnlyTimeStampV2"})
if(usp/XMLName{elem, XADESv141_NS})
THROW("%s is not supported", elem);
for(const char *elem: {"CompleteCertificateRefs", "CompleteRevocationRefs", "SigAndRefsTimeStamp", "TimeStampValidationData"})
if(usp/elem)
WARN("%s are not supported", elem);
Expand Down
74 changes: 36 additions & 38 deletions src/SignatureXAdES_LTA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace digidoc
constexpr XMLName ArchiveTimeStamp {"ArchiveTimeStamp", XADESv141_NS};
}

void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod) const
void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view canonicalizationMethod, XMLNode ts) const
{
for(auto ref = signature/"SignedInfo"/"Reference"; ref; ref++)
{
Expand All @@ -64,7 +64,7 @@ void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view can
if(file == files.cend())
THROW("Filed to find reference URI in container");

static_cast<const DataFilePrivate*>(*file)->digest(digest);
dynamic_cast<const DataFilePrivate*>(*file)->digest(digest);
}

for(const auto *name: {"SignedInfo", "SignatureValue", "KeyInfo"})
Expand All @@ -75,65 +75,60 @@ void SignatureXAdES_LTA::calcArchiveDigest(const Digest &digest, string_view can
DEBUG("Element %s not found", name);
}

auto usp = unsignedSignatureProperties();
for(const auto *name: {
"SignatureTimeStamp",
"CounterSignature",
"CompleteCertificateRefs",
"CompleteRevocationRefs",
"AttributeCertificateRefs",
"AttributeRevocationRefs",
"CertificateValues",
"RevocationValues",
"SigAndRefsTimeStamp",
"RefsOnlyTimeStamp" })
for(auto elem: unsignedSignatureProperties())
{
if(auto elem = usp/name)
signatures->c14n(digest, canonicalizationMethod, elem);
else
DEBUG("Element %s not found", name);
}

if(auto elem = usp/XMLName{"TimeStampValidationData", XADESv141_NS})
if(elem == ts)
break;
signatures->c14n(digest, canonicalizationMethod, elem);
else
DEBUG("Element TimeStampValidationData not found");
}
//ds:Object
}

void SignatureXAdES_LTA::extendSignatureProfile(const string &profile)
{
SignatureXAdES_LT::extendSignatureProfile(profile);
if(SignatureXAdES_LTA::profile().find(ASiC_E::ASIC_TS_PROFILE) == string::npos)
SignatureXAdES_LT::extendSignatureProfile(profile);
if(profile != ASiC_E::ASIC_TSA_PROFILE)
return;

int i = 0;
for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; ts; ts++, ++i);

Digest calc;
auto method = canonicalizationMethod();
calcArchiveDigest(calc, method);
calcArchiveDigest(calc, method, {});

TS tsa(CONF(TSUrl), calc);
auto ts = unsignedSignatureProperties() + ArchiveTimeStamp;
ts.setNS(ts.addNS(XADESv141_NS, "xades141"));
ts.setProperty("Id", id() + "-A0");
ts.setProperty("Id", id() + "-A" + to_string(i));
(ts + CanonicalizationMethod).setProperty("Algorithm", method);
ts + EncapsulatedTimeStamp = tsa;
}

TS SignatureXAdES_LTA::tsaFromBase64() const
X509Cert SignatureXAdES_LTA::ArchiveTimeStampCertificate() const
{
try {
return {unsignedSignatureProperties()/ArchiveTimeStamp/EncapsulatedTimeStamp};
} catch(const Exception &) {}
return {};
if(auto list = ArchiveTimeStamps(); !list.empty())
return list.back().cert;
return X509Cert();
}

X509Cert SignatureXAdES_LTA::ArchiveTimeStampCertificate() const
string SignatureXAdES_LTA::ArchiveTimeStampTime() const
{
return tsaFromBase64().cert();
if(auto list = ArchiveTimeStamps(); !list.empty())
return list.back().time;
return {};
}

string SignatureXAdES_LTA::ArchiveTimeStampTime() const
vector<TSAInfo> SignatureXAdES_LTA::ArchiveTimeStamps() const
{
return date::to_string(tsaFromBase64().time());
vector<TSAInfo> result;
for(auto ts = unsignedSignatureProperties()/ArchiveTimeStamp; ts; ts++)
{
TS t(ts/EncapsulatedTimeStamp);
result.push_back({t.cert(), util::date::to_string(t.time())});
}
return result;
}

void SignatureXAdES_LTA::validate(const string &policy) const
Expand All @@ -157,9 +152,12 @@ void SignatureXAdES_LTA::validate(const string &policy) const
auto ts = unsignedSignatureProperties()/ArchiveTimeStamp;
if(!ts)
THROW("Missing ArchiveTimeStamp element");
verifyTS(ts, exception, [this](const Digest &digest, string_view canonicalizationMethod) {
calcArchiveDigest(digest, canonicalizationMethod);
});
for(; ts; ts++)
{
verifyTS(ts, exception, [this, ts](const Digest &digest, string_view canonicalizationMethod) {
calcArchiveDigest(digest, canonicalizationMethod, ts);
});
}
} catch(const Exception &e) {
exception.addCause(e);
}
Expand Down
4 changes: 2 additions & 2 deletions src/SignatureXAdES_LTA.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ class SignatureXAdES_LTA final: public SignatureXAdES_LT

X509Cert ArchiveTimeStampCertificate() const final;
std::string ArchiveTimeStampTime() const final;
std::vector<TSAInfo> ArchiveTimeStamps() const final;
void validate(const std::string &policy) const final;
void extendSignatureProfile(const std::string &profile) final;

private:
DISABLE_COPY(SignatureXAdES_LTA);

void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod) const;
TS tsaFromBase64() const;
void calcArchiveDigest(const Digest &digest, std::string_view canonicalizationMethod, XMLNode node) const;
};

}
5 changes: 5 additions & 0 deletions src/XMLDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ struct XMLElem
return bool(d);
}

constexpr bool operator==(XMLElem other) const noexcept
{
return d == other.d;
}

constexpr auto& operator++() noexcept
{
d = d ? find(d->next, d->type) : nullptr;
Expand Down
7 changes: 7 additions & 0 deletions src/digidoc-tool.1.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ Command sign:
--tsurl - option to change TS URL (default http://demo.sk.ee/tsa)
--dontValidate - Don't validate container on signature creation

Command extend:
Example: " << executable << " extend --signature=0 demo-container.asice
Available options:
--profile= - signature profile, TS, TSA, time-stamp, time-stamp-archive
--signature= - signature to extend
--dontValidate - Don't validate container on signature creation

All commands:
--nocolor - Disable terminal colors
--loglevel=[0,1,2,3,4] - Log level 0 - none, 1 - error, 2 - warning, 3 - info, 4 - debug
Expand Down
Loading

0 comments on commit 6c5fa6b

Please sign in to comment.