Logo Search packages:      
Sourcecode: gdcm version File versions  Download package

AnonymizeAES.cxx

/*=========================================================================

  Program: GDCM (Grassroots DICOM). A DICOM library
  Module:  $URL$

  Copyright (c) 2006-2009 Mathieu Malaterre
  All rights reserved.
  See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/* 
 * The following is a simple proof of concept to show we are capable of implementing
 * C.12.1.1.4 Encrypted Attribute Descriptions (PS 3.3-2008)
 * 
 * for now we only encrypt two attribute: Patient Name & Patient ID
 *
 * Usage:
 *  AnonymizeAES gdcmData/012345.002.050.dcm out.dcm
 */

/*
 * Note. To generate a rsa key pair on linux simply do:
 * $ ssh-keygen -t rsa
 * choose empty passphrase (??)
 * TODO: what is ssh-keygen -t dsa ? Apparently this is not supported by polarssl 0.10.1
 *
 * To install xyssl
 * $ sudo apt-get install libxyssl-dev 
 */

#include "gdcmAES.h"
#include "gdcmReader.h"
#include "gdcmImage.h"
#include "gdcmWriter.h"
#include "gdcmAttribute.h"
#include "gdcmImageWriter.h"
#include "gdcmSequenceOfItems.h"
#include "gdcmAnonymizer.h"
#include "gdcmExplicitDataElement.h"
#include "gdcmSwapper.h"
#include "gdcmUIDGenerator.h"
#include "gdcmFileExplicitFilter.h"

#include <iostream>
#include <fstream>

int main(int argc, char *argv[])
{
  if( argc < 3 )
    {
    std::cerr << argv[0] << " input.dcm output.dcm" << std::endl;
    return 1;
    }
  const char *filename = argv[1];
  const char *outfilename = argv[2];

  gdcm::Reader reader;
  reader.SetFileName( filename );
  if( !reader.Read() )
    {
    std::cerr << "Could not read: " << filename << std::endl;
    return 1;
    }

  // The output of gdcm::Reader is a gdcm::File
  gdcm::File &file = reader.GetFile();

  // the dataset is the the set of element we are interested in:
  gdcm::DataSet &ds = file.GetDataSet();

  // Let's do only Patient Name / Patient ID
  gdcm::Tag tag1(0x10,0x10);
  gdcm::Tag tag2(0x10,0x20);

  // Create an instance of the Encrypted Attributes DataSet
/*
Modified Attributes Sequence (0400,0550) 1
Sequence of Items containing all Attributes
that were removed or replaced by "dummy
values" in the main dataset during deidentification
of the SOP instance. Upon
reversal of the de-identification process, the
Attributes are copied back into the main
dataset, replacing any dummy values that
might have been created. Only a single
Item shall be present.
 */
  // Create a Sequence
  gdcm::SmartPointer<gdcm::SequenceOfItems> sq = new gdcm::SequenceOfItems();
  sq->SetLengthToUndefined();

    // Create a *single* item
    gdcm::Item it;
    it.SetVLToUndefined();
    gdcm::DataSet &encryptedds = it.GetNestedDataSet();
    encryptedds.Insert( ds.GetDataElement( tag1 ) );
    encryptedds.Insert( ds.GetDataElement( tag2 ) );

    sq->AddItem(it);

  gdcm::DataElement des( gdcm::Tag(0x0400,0x0550) );
  des.SetVR(gdcm::VR::SQ);
  des.SetValue(*sq);
  des.SetVLToUndefined();
  
  //ds.Insert(des);


  std::ostringstream os;
  std::cout << des << std::endl;
  des.Write<gdcm::ExplicitDataElement,gdcm::SwapperNoOp>(os);

  std::string encrypted_str = os.str();
  std::cout << "Size:" <<  encrypted_str.size() << std::endl;
  size_t encrypted_len = encrypted_str.size();

/*
Note: 1. Content encryption may require that the content (the DICOM Data Set) be padded to a
multiple of some block size. This shall be performed according to the Content-encryption
Process defined in RFC-2630.
*/
  if( encrypted_len % 16 != 0 )
    {
    encrypted_len = ((encrypted_len / 16) + 1) * 16;
    }
  char *buf = new char[ encrypted_len ];
  memset( buf, 0, encrypted_len );
  memcpy( buf, encrypted_str.c_str(), encrypted_str.size() );

  char key[32] = {};
  gdcm::AES aes;
  if( !aes.SetkeyEnc( key, 128 ) ) return 1;
  char iv[16] = {};
  aes.CryptCbc( gdcm::AES::ENCRYPT, encrypted_len, iv, buf, buf );

{
  // Create a Sequence
  gdcm::SmartPointer<gdcm::SequenceOfItems> sq = new gdcm::SequenceOfItems();
  sq->SetLengthToUndefined();

  gdcm::TransferSyntax encrypted_ts = gdcm::TransferSyntax::ExplicitVRLittleEndian;
  // <entry group="0400" element="0510" vr="UI" vm="1" name="Encrypted Content Transfer Syntax UID"/>
  gdcm::DataElement encrypted_ts_de( gdcm::Tag(0x400,0x510) );
  encrypted_ts_de.SetByteValue( encrypted_ts.GetString(), strlen(encrypted_ts.GetString()) );
  // <entry group="0400" element="0520" vr="OB" vm="1" name="Encrypted Content"/>
  gdcm::DataElement encrypted_de( gdcm::Tag(0x400,0x520) );
  encrypted_de.SetByteValue( (char*)buf, encrypted_len );
  delete[] buf;

    // Create an item
    gdcm::Item it;
    it.SetVLToUndefined();
    gdcm::DataSet &nds = it.GetNestedDataSet();
    nds.Insert(encrypted_ts_de);
    nds.Insert(encrypted_de);

    sq->AddItem(it);


  // Insert sequence into data set
  gdcm::DataElement des( gdcm::Tag(0x0400,0x0500) );
  des.SetVR(gdcm::VR::SQ);
  des.SetValue(*sq);
  des.SetVLToUndefined();
  
  ds.Insert(des);
}


  gdcm::Anonymizer ano;
  ano.SetFile( reader.GetFile() );
  ano.Replace( tag1, "D'oh! Patient Name is in Encrypted Content"); // Patient Name is 1C
  ano.Replace( tag2, "-1" ); // Patient ID is 1C
  ano.RemoveGroupLength();
  ano.RemovePrivateTags();

  // PS 3.15
  // E.1 BASIC APPLICATION LEVEL CONFIDENTIALITY PROFILE
  // 6. The attribute Patient Identity Removed (0012,0062) shall be replaced or added to the dataset with a
  // value of YES, and a value inserted in De-identification Method (0012,0063) or De-identification
  // Method Code Sequence (0012,0064).
  ano.Replace( gdcm::Tag(0x0012,0x0062), "YES");
  ano.Replace( gdcm::Tag(0x0012,0x0063), "BASIC APPLICATION LEVEL CONFIDENTIALITY PROFILE");

  gdcm::FileMetaInformation::SetSourceApplicationEntityTitle( "AnonymizeAES" );
    gdcm::FileMetaInformation &fmi = file.GetHeader();
    //fmi.Remove( gdcm::Tag(0x0002,0x0003) ); // will be regenerated
    fmi.Remove( gdcm::Tag(0x0002,0x0012) ); // will be regenerated
    fmi.Remove( gdcm::Tag(0x0002,0x0013) ); //  '   '    '

// Since the de-identified SOP Instance is a significantly altered version of the original Data Set, it is
// a new SOP Instance, with a SOP Instance UID that differs from the original Data Set.
{
//        ds.Remove( gdcm::Tag(0x008,0x0018) );
        gdcm::DataElement de( gdcm::Tag(0x008,0x0018) ); // Instance
  gdcm::UIDGenerator uid;
  std::string instance_uid = uid.Generate();
        de.SetByteValue( instance_uid.c_str(), instance_uid.size() );
        de.SetVR( gdcm::Attribute<0x008, 0x0018>::GetVR() );
        ds.Replace( de );

}
      gdcm::FileExplicitFilter fef;
      fef.SetFile( reader.GetFile() );
      if(!fef.Change())
        {
        std::cerr << "Failed to change: " << filename << std::endl;
        return 1;
        }

  gdcm::Writer writer;
  writer.SetFile( reader.GetFile() );
  writer.SetFileName( outfilename );
  if( !writer.Write() )
    {
    return 1;
    }

  return 0;
}


Generated by  Doxygen 1.6.0   Back to index