/* * $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. <www.di-mgt.com.au> <www.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://www.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; } /*****************************************************************************************************/