/*
* $Id: TestSc14n.c $
* Last updated:
* $Date: 2019-12-13 21:07 $
* $Version: 2.1.0 $
*/
/* Some tests using the Sc14n C/C++ interface.
* Please report any bugs to <http://cryptosys.net/contact/>
*/
/******************************* LICENSE ***********************************
* Copyright (C) 2017-19 David Ireland, DI Management Services Pty Limited.
* All rights reserved. <https://di-mgt.com.au> <https://cryptosys.net>
* The code in this module is licensed under the terms of the MIT license,
* unless otherwise marked.
* For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "diSc14n.h"
/*
* Requires Sc14n to be installed on your system, available from <https://cryptosys.net/sc14n/>.
* In particular that `diSc14n.dll` is in your library path.
* You must link to `diSc14n.lib`. In MSVC++ IDE, use
* Project > Properties > Input > Additional Dependencies and add the full path to `diSc14n.lib`.
* E.g.
* Additional Dependencies = $(OutDir)diSc14n.lib;%(AdditionalDependencies)
* Using command-line:
* CL TestSc14n.c /link ..\Release\diSc14n.lib
*
* Test files, e.g. `olamundo.xml`, are in `sc14n-testfiles.zip`.
*/
#ifdef NDEBUG
/* Make sure assertion testing is turned on */
#undef NDEBUG
#endif
#include <assert.h>
/* Declaration for 3rd-party function with code below */
char *replace_str(const char *str, const char *old, const char *news);
/* File utility */
static char *read_file_to_string(const char *fname) {
static char *buf;
FILE *fp;
long flen;
fp = fopen(fname, "rb");
if (!fp) return NULL;
fseek(fp, 0, SEEK_END);
flen = ftell(fp);
if (flen < 0) return NULL;
buf = calloc(1, flen + 1);
rewind(fp);
fread(buf, 1, flen, fp);
fclose(fp);
// User to free memory allocation
return buf;
}
/* DO THE BUSINESS... */
int main(void)
{
long r, n, ver, nchars;
char *fname, *oname;
char *cp, *news, *fdata;
size_t filesize;
char buf[256];
char digval[SC14N_MAX_DIGEST_CHARS + 1];
char digval1[SC14N_MAX_DIGEST_CHARS + 1];
char *digok;
/* General information about the core library DLL */
ver = SC14N_Gen_Version();
printf("DLL Version=%ld\n", ver);
printf("LicenceType=%c\n", SC14N_Gen_LicenceType());
nchars = SC14N_Gen_ModuleName(NULL, 0, 0);
if (nchars > 0) {
cp = malloc(nchars + 1);
nchars = SC14N_Gen_ModuleName(cp, nchars, 0);
printf("Module=%s\n", cp);
free(cp);
}
nchars = SC14N_Gen_CompileTime(NULL, 0);
if (nchars > 0) {
cp = malloc(nchars + 1);
nchars = SC14N_Gen_CompileTime(cp, nchars);
printf("Compiled=%s\n", cp);
free(cp);
}
assert(nchars > 0);
nchars = SC14N_Gen_Platform(buf, sizeof(buf) - 1);
printf("Platform=%s\n", buf);
// DO TESTS ON INPUT.XML (these are from the manual)
printf("FILE: %s\n", "input.xml");
// Example 1. Excludes the first element with the tag name <Signature>
r = C14N_File2File("c14nfile1.txt", "input.xml", "Signature", "", SC14N_TRAN_OMITBYTAG);
assert(0 == r);
// Example 2. Finds and transforms the first element with the tag name <SignedInfo>
r = C14N_File2File("c14nfile2.txt", "input.xml", "SignedInfo", "", SC14N_TRAN_SUBSETBYTAG);
assert(0 == r);
// Example 3. Finds and transforms the third element with the tag name <Data>
r = C14N_File2File("c14nfile3.txt", "input.xml", "Data[3]", "", SC14N_TRAN_SUBSETBYTAG);
assert(0 == r);
// Example 4. Finds and transforms the element with attribute Id="foo"
r = C14N_File2File("c14nfile4.txt", "input.xml", "foo", "", SC14N_TRAN_SUBSETBYID);
assert(0 == r);
// Example 5. Finds and transforms the element with attribute ID="bar"
r = C14N_File2File("c14nfile5.txt", "input.xml", "ID=bar", "", SC14N_TRAN_SUBSETBYID);
assert(0 == r);
// Example 6. Excludes element with attribute Id="thesig"
r = C14N_File2File("c14nfile6.txt", "input.xml", "thesig", "", SC14N_TRAN_OMITBYID);
assert(0 == r);
// REDO LAST TESTS BUT COMPUTE DIGEST INSTEAD OF CREATING NEW FILE
// Example 1. Excludes the first element with the tag name <Signature>
r = C14N_File2Digest(digval, sizeof(digval) - 1, "input.xml", "Signature", "", SC14N_TRAN_OMITBYTAG);
printf("DIG1=%s\n", digval);
assert(strlen(digval) > 0);
// Example 2. Finds and transforms the first element with the tag name <SignedInfo>
r = C14N_File2Digest(digval, sizeof(digval) - 1, "input.xml", "SignedInfo", "", SC14N_TRAN_SUBSETBYTAG);
printf("DIG2=%s\n", digval);
assert(strlen(digval) > 0);
// Example 3. Finds and transforms the third element with the tag name <Data>
r = C14N_File2Digest(digval, sizeof(digval) - 1, "input.xml", "Data[3]", "", SC14N_TRAN_SUBSETBYTAG);
printf("DIG3=%s\n", digval);
assert(strlen(digval) > 0);
// Example 4. Finds and transforms the element with attribute Id="foo"
r = C14N_File2Digest(digval, sizeof(digval) - 1, "input.xml", "foo", "", SC14N_TRAN_SUBSETBYID);
printf("DIG4=%s\n", digval);
assert(strlen(digval) > 0);
// Example 5. Finds and transforms the element with attribute ID="bar"
r = C14N_File2Digest(digval, sizeof(digval) - 1, "input.xml", "ID=bar", "", SC14N_TRAN_SUBSETBYID);
printf("DIG5=%s\n", digval);
assert(strlen(digval) > 0);
// Example 6. Excludes element with attribute Id="thesig"
r = C14N_File2Digest(digval, sizeof(digval) - 1, "input.xml", "thesig", "", SC14N_TRAN_OMITBYID);
printf("DIG6=%s\n", digval);
assert(strlen(digval) > 0);
printf("NOTE: Should have DIG1==DIG6 and DIG3==DIG4 above.\n");
// Transform entire file
fname = "olamundo.xml";
oname = "olamundo-out.xml";
printf("FILE: %s\n", fname);
n = C14N_File2File(oname, fname, "", "", 0);
printf("C14N_File2File()->%s returns %ld (expected 0 => OK)\n", oname, n);
assert(0 == n);
// Compute digest of entire file
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "", "", 0);
printf("C14N_File2Digest(%s)=\t%s\n", fname, digval);
// and do the same to the transformed file we made above
r = C14N_File2Digest(digval1, sizeof(digval1) - 1, oname, "", "", 0);
printf("C14N_File2Digest(%s)=\t%s\n", oname, digval1);
assert(0 == strcmp(digval, digval1));
// Same data, different encoding plus UTF-8 Byte Order Mark
fname = "olamundo-utf8bom.xml";
oname = "olamundo-utf8bom-out.xml";
printf("FILE: %s\n", fname);
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "", "", 0);
printf("C14N_File2Digest(%s)=\t%s\n", fname, digval);
// -- we can transform (X)HTML files, too.
// This example is used in a detached signature: see `detached.xml`
fname = "abc.html";
oname = "abc-c14n-html.txt";
printf("FILE: %s\n", fname);
n = C14N_File2File(oname, fname, "", "", 0);
printf("C14N_File2File()->%s returns %ld (expected 0 => OK)\n", oname, n);
assert(0 == n);
// Display entire file after transforming...
nchars = C14N_File2String(NULL, 0, fname, "", "", 0);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_File2String(cp, nchars, fname, "", "", 0);
printf("C14N('%s'):\n", fname);
printf("%s\n", cp);
free(cp);
// Compute SHA-1 digest of entire file
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "", "", 0);
printf("SHA1(%s)=\t%s\n", fname, digval);
// and do the same to the transformed file we made above
r = C14N_File2Digest(digval1, sizeof(digval1) - 1, oname, "", "", 0);
printf("SHA1(%s)=\t%s\n", oname, digval1);
assert(0 == strcmp(digval, digval1));
// Same again but using SHA-256
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "", "", SC14N_DIG_SHA256);
printf("SHA256(%s)=\t%s\n", fname, digval);
// Extract and transform the body element
// Output to a byte array
fname = "olamundo-base.xml";
printf("FILE: %s\n", fname);
nchars = C14N_File2String(NULL, 0, fname, "Body", "", SC14N_TRAN_SUBSETBYTAG);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_File2String(cp, nchars, fname, "Body", "", SC14N_TRAN_SUBSETBYTAG);
printf("C14N_File2String(BODY) returns %ld bytes.\n", nchars);
// Note this a byte array containing UTF-8-encoded text, so it may not print OK on a Windows console
printf("C14N(Body) [may display funny]:\n");
printf("%s\n", cp);
free(cp);
// Compute SHA-1 digest value of the XML document excluding the <Signature> element
// ... this is the value to be inserted into <SignedInfo>
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "Signature", "", SC14N_TRAN_OMITBYTAG);
printf("DIGVAL:\n");
printf("%s\n", digval);
// Extract the SignedInfo element into memory
// Note %digval% parameter to be completed in the <DigestValue> element
nchars = C14N_File2String(NULL, 0, fname, "SignedInfo", "", SC14N_TRAN_SUBSETBYTAG);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_File2String(cp, nchars, fname, "SignedInfo", "", SC14N_TRAN_SUBSETBYTAG);
printf("SIGNEDINFO (BASE):\n");
printf("%s\n", cp);
// Insert the required DigestValue we prepared earlier
news = replace_str(cp, "%digval%", digval);
printf("SIGNEDINFO (COMPLETED):\n");
printf("%s\n", news);
// Now compute the digest value of this updated string.
// We could use this to compute the required signature value
// - see TestSc14nPKI.c
r = C14N_String2Digest(digval1, sizeof(digval1) - 1, news, strlen(news), "", "", 0);
printf("SHA1(signedinfo)= %s\n", digval1);
free(cp);
free(news);
/* EXCLUSIVE C14N */
// Example files from RFC 3741 "Exclusive XML Canonicalization, Version 1.0", s2.2
// after errata reference E01 2002-10-03 (xml:space="retain" should be xml:space="preserve")
// Two example files `example1.xml` and `example 2.xml`: compute c14n of element <n1:elem2>
// Read in first file to a string then display it
fname = "example1.xml";
fdata = read_file_to_string(fname);
assert(fdata);
filesize = strlen(fdata);
printf("\nFILE: %s\n", fname);
printf("%s\n", fdata);
// Transform using inclusive c14n
nchars = C14N_String2String(NULL, 0, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_String2String(cp, nchars, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG);
printf("1.1 Inclusive c14n of element 'n1:elem2':\n");
printf("%s\n", cp);
free(cp);
// Transform using exclusive c14n
nchars = C14N_String2String(NULL, 0, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG | SC14N_METHOD_EXCLUSIVE);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_String2String(cp, nchars, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG | SC14N_METHOD_EXCLUSIVE);
printf("1.2 Exclusive c14n of element 'n1:elem2':\n");
printf("%s\n", cp);
free(cp);
// and compute SHA-1 digest for reference
r = C14N_String2Digest(digval, sizeof(digval) - 1, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG | SC14N_METHOD_EXCLUSIVE);
printf("SHA1(ex1.2)=%s\n", digval);
// Free file data
free(fdata);
// Second file
fname = "example2.xml";
fdata = read_file_to_string(fname);
assert(fdata);
filesize = strlen(fdata);
printf("\nFILE: %s\n", fname);
printf("%s\n", fdata);
// Transform using inclusive c14n
nchars = C14N_String2String(NULL, 0, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_String2String(cp, nchars, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG);
printf("2.1 Inclusive c14n of element 'n1:elem2':\n");
printf("%s\n", cp);
free(cp);
// Transform using exclusive c14n
nchars = C14N_String2String(NULL, 0, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG | SC14N_METHOD_EXCLUSIVE);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_String2String(cp, nchars, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG | SC14N_METHOD_EXCLUSIVE);
printf("2.2 Exclusive c14n of element 'n1:elem2':\n");
printf("%s\n", cp);
free(cp);
// and compute SHA-1 digest for reference
r = C14N_String2Digest(digval1, sizeof(digval1) - 1, fdata, filesize, "n1:elem2", "", SC14N_TRAN_SUBSETBYTAG | SC14N_METHOD_EXCLUSIVE);
printf("SHA1(ex2.2)=%s\n", digval1);
// Free file data
free(fdata);
printf("\nCOMMENT: Excl-c14n 1.2 and 2.2 above should be identical\n");
printf("SHA1(ex1.2)=%s\n", digval);
printf("SHA1(ex2.2)=%s\n", digval1);
assert(0 == strcmp(digval, digval1));
// [v2.1] DEMONSTRATE FLATTEN OPTION
fname = "ignorable_ws.xml";
printf("\nFILE: %s\n", fname);
// Default c14n...
digok = "JNluoz+Z+MbLrTX8W//wEEgeFpo=";
nchars = C14N_File2String(NULL, 0, fname, "", "", 0);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_File2String(cp, nchars, fname, "", "", 0);
printf("no-flatten:\n");
printf("%s\n", cp);
free(cp);
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "", "", 0);
printf("DIGVAL=%s\n", digval);
assert(0 == strcmp(digval, digok));
// Flatten the XML - remove ignorable whitespace
digok = "4ZKWJnP7dUperStlOKrq7athzxw=";
nchars = C14N_File2String(NULL, 0, fname, "", "", SC14N_OPT_FLATTEN);
assert(nchars > 0);
cp = malloc(nchars + 1);
nchars = C14N_File2String(cp, nchars, fname, "", "", SC14N_OPT_FLATTEN);
printf("FLATTEN:\n");
printf("%s\n", cp);
free(cp);
r = C14N_File2Digest(digval, sizeof(digval) - 1, fname, "", "", SC14N_OPT_FLATTEN);
printf("DIGVAL=%s\n", digval);
assert(0 == strcmp(digval, digok));
printf("\nALL DONE.\n");
}
/* THIRD-PARTY CODE ********************************************************************************/
/* Ref: http://creativeandcritical.net/str-replace-c/
* Description: Replaces in the string str all the occurrences of the source string old
* with the destination string new. The parameters old and new may be of any length,
* and their lengths are allowed to differ.
* None of the three parameters may be NULL.
*
* Returns: The post-replacement string, or NULL if memory for the new string could not be allocated.
* Does not modify the original string. The memory for the returned post-replacement
* string may be deallocated with the standard library function free() when it is no longer required.
*
* Licence: Public domain. You may use this code in any way you see fit,
* optionally crediting its author (me, Laird Shaw, with assistance from comp.lang.c).
* http://creativeandcritical.net/contact/
*/
char *replace_str(const char *str, const char *old, const char *news)
{
char *ret, *r;
const char *p, *q;
size_t oldlen = strlen(old);
size_t count, retlen, newlen = strlen(news);
if (oldlen != newlen) {
for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen)
count++;
/* this is undefined if p - str > PTRDIFF_MAX */
retlen = p - str + strlen(p) + count * (newlen - oldlen);
}
else
retlen = strlen(str);
if ((ret = malloc(retlen + 1)) == NULL)
return NULL;
for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) {
/* this is undefined if q - p > PTRDIFF_MAX */
ptrdiff_t l = q - p;
memcpy(r, p, l);
r += l;
memcpy(r, news, newlen);
r += newlen;
}
strcpy(r, p);
return ret;
}
/*****************************************************************************************************/