AN INVESTIGATION OF SCALABLE AS A COVER MEDIUM FOR

STEGANOGRAPHY

By

Gerard J. Hungerman

Submitted to the

Faculty of the College of Arts and Sciences

of American University

in Partial Fulfillment, of

the Requirements for the Degree of

M aster of Science

In

Computer Science

Chair:

Dr. Michael Gray

Dr. Angela Wu ^ i I

Dr. Dean McCullough

Dean of the College of Arts and Sciences

Date

2006

American University

Washington, D.C. 20016 AMERICAN UNIVERSITY LIBRARY

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. UMI Number: 1439943

INFORMATION TO USERS

The quality of this reproduction is dependent upon the quality of the copy submitted. Broken or indistinct print, colored or poor quality illustrations and photographs, print bleed-through, substandard margins, and improper alignment can adversely affect reproduction. In the unlikely event that the author did not send a complete manuscript and there are missing pages, these will be noted. Also, if unauthorized copyright material had to be removed, a note will indicate the deletion.

® UMI

UMI Microform 1439943 Copyright 2007 by ProQuest Information and Learning Company. All rights reserved. This microform edition is protected against unauthorized copying under Title 17, United States Code.

ProQuest Information and Learning Company 300 North Zeeb Road P.O. Box 1346 Ann Arbor, Ml 48106-1346

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. AN INVESTIGATION OF AS A COVER MEDIUM FOR

STEGANOGRAPHY

BY

Gerard J. Hungerman

ABSTRACT

Cryptography is normally used when security is required in Internet communications, even

though its use makes obvious to outside observers that communications are sensitive. If the true

desire is for the very existence of sensitive communication to go undetected, then steganography is

the answer. W ith steganography, data can be hidden in ordinary digital files such th at any unwanted

interceptors of communications are likely to think that all they have are normal messages.

A that has promise of becoming widely used on the Internet is Scalable

Vector Graphics (SVG), which supports the delivery of two-dimensional graphical images with

rich content. This work proposes algorithms for embedding information into SVG and implements

three different approaches, each with variations to further explore the nature of the embedding

techniques. Preliminary testing has been conducted to show the viability of SVG as a cover medium

for steganography.

ii

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. ACKNOWLEDGEMENTS

I would like to acknowledge everyone who helped me to reach this point in my academic

career. I would like to thank the faculty of the American University Computer Science Department,

Dr. Michael Gray and Dr. Angela Wu, for their support and counsel. I would especially like to

thank Dr. Dean McCullough not only for his advice and review of this work, but also for his

understanding and friendship through the process. Most of all, I want to thank anyone who ever

reached out to me when I was in need, whether it was to help at work, home, or to just motivate me

to complete this body of work. Thank you Elise for your love and support through this balancing

act. Last but certainly not least, I love my family beyond words, and cannot thank them enough

for their love and support.

iii

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. TABLE OF CONTENTS

ABSTRACT ...... ii

ACKNOWLEDGEMENTS...... iii

LIST OF TABLES ...... vi

LIST OF ILLUSTRATIONS ...... vii

LISTINGS ...... viii

CHAPTER

1. INTRODUCTION ...... 1

2. B A C K G R O U N D ...... 3

2.1 S teganography ...... 3

2.1.1 Principles of Steganography ...... 4

2.1.2 T echniques ...... 7

2.1.3 Steganalysis ...... 9

2.1.4 Attacks...... 11

2.1.5 Security and Robustness ...... 12

2.2 Scalable Vector Graphics ...... 13

2.2.1 Raster Im a g e s ...... 13

2.2.2 Vector Im ages ...... 14

2.2.3 The Scalable Vector Graphics (SVG) Form at ...... 15

3. COVERT CHANNELS IN SV G ...... 22

3.1 Embedding M ethods ...... 23

3.1.1 In s e rtio n ...... 24

3.1.2 Substitution ...... 27

3.1.3 Tweaking ...... 29

iv

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 3.1.4 Other Embedding Approaches ...... 32

3.2 Im p le m e n ta tio n ...... 32

3.2.1 Inserting W hite Space ...... 32

3.2.2 Adjusting Least Significant Figures ...... 34

3.2.3 Reordering Information ...... 37

3.3 Test Results ...... 40

3.3.1 B an d w id th ...... 41

3.3.2 Visual and Structural Effects ...... 44

3.3.3 Compression Testing ...... 47

3.3.4 Statistical Analysis ...... 48

4. CONCLUSIONS AND FUTURE WORK ...... 51

APPENDIX

A. SVG FILES USED IN TESTING ...... 53

B. SOURCE C O D E ...... 58

B.l Overview ...... 58

B.1.1 Inserting White Space ...... 60

B.1.2 Adjusting Least Significant Figures ...... 60

B.1.3 Reordering Information ...... 61

B.1.4 Combining Methods ...... 62

B.2 Source Code Listings ...... 63

REFERENCES...... 131

v

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. LIST OF TABLES

Table Page

3.1. Attributes Allowing Adjustment ...... 35

3.2. Algorithms and Tools T e s t e d ...... 41

3.3. Bandwidth in Initial T esting ...... 42

3.4. Bandwidth in Testing of alg_TweakTwoNumsMinErr ...... 43

3.5. Compression Testing Results ...... 47

3.6. Confidence Values that Cover and Stego are Different ...... 49

A.I. SVG Files Used in Testing ...... 53

vi

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. LIST OF ILLUSTRATIONS

Figure Page

2.1. Hello World SVG Example Im ag e ...... 17

3.1. Plot of RMS vs. B an d w id th ...... 44

3.2. Visual Effects of Tw eaking...... 46

A.I. animated_bustrack.svg After Animation ...... 54

A.2. animated-plane. svg During Animation ...... 55

A.3. gaenseblume. svg During Animation ...... 55

A.4. krokus. svg During Anim ation ...... 56

A.5. V ienna.s v g ...... 56

A.6. w o r ld .p o p u la tio n .s v g ...... 57

vii

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. LISTINGS

Listing Page

2.1 Hello World SVG Example Source...... 18

B.l SVGSteg.java...... 63

B.2 StegMethod. j ava ...... 65

B.3 StegAlgorithm.java ...... 66

B.4 InfoGen.java...... 67

B.5 TextManip.java...... 69

B.6 SVGTag.java...... 74

B.7 SVGAttribute.java ...... 78

B.8 TagProcessor.java ...... 79

B.9 LeadTab.java...... 81

B.10 LeadTabSqOff. j a v a ...... 86

B .ll TweakNums. j a v a ...... 92

B.12 TweakTwoNums. j a v a ...... 100

B.13 TweakTwoNumsMinErr. java ...... 102

B.14 OrderEncoder. ja v a ...... 108

B.15 ReOrderElements. j a v a ...... I l l

B.16 OrderedSVGDoc. java ...... 115

B.17 OrderedSVGTag. java ...... 119

B.18 SVGDocElem. java ...... 123

B.19 ReOrderAtts. ja v a...... 124

B.20 AllMethods. java ...... 126

viii

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. CHAPTER 1

INTRODUCTION

The rise of the Internet has provided a medium of communication where large amounts of

data can be sent around the world faster than ever before. A person from New Zealand, one from

India, and another from the United States can hold a group chat from the comfort of their respective

homes. Even those with limited speech rights have found a potential outlet. Looking to bring part

of the global economy home, countries with oppressive governments have embraced the Internet for

e-commerce, but in turn have not allowed their people full access to World Wide Web [54]. Certain

“restricted” content is blocked from view, and people’s communications are monitored. The fight

to view any Internet content, now to include increasingly popular blogs, continues today. In some

cases, anonymizers and foreign proxy servers do the trick until they are found out and blocked. The

more powerful the government, the more sophisticated the blocking technology [26].

As far as communications monitoring, the immediate answer that comes to mind is to use

cryptography. It has been the answer for ensuring the protection of credit card numbers and other

sensitive information in transit, to include the digital signatures on new eCheck technology [2].

There are some major problems though with using cryptography alone for the purpose of evading

oppressive government monitoring. First and foremost, the technology is typically banned in these

countries, and the United States, not wanting this technology in the hands of its enemies, has strict

export controls on it. Another important point is that even if a person under a monitoring regime

were to get their hands on a cryptographic engine, any messages sent using it would be flagged

as suspicious, giving the government reason to monitor the sender. Even if the sender manages to

accomplish anonymity, the message could still be easily blocked.

Therefore, the requirement is further refined to be secretive communication such that

outside monitoring does not recognize that secretive communications are taking place. For example,

1

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 2

an e-mail detailing a vacation experience to a friend with amateur photography attached draws much

less attention than one containing random cipher text, yet the vacation experience can contain

much more than meets the eye. With steganography, data can be hidden in ordinary digital files

such that any unwanted interceptors of the message are likely to think that all they have is a

normal message, and not spend time trying to crack something that in their mind does not exist.

Hence, cryptography and steganography work well together, the former scrambling a message into

randomness and the latter tucking it safely away where no one, and hopefully no machine, can see

it. With steganography added to the arsenal, a sender can use open communication lines for covert

communication.

Steganography is certainly not a new idea, as examples of its use date back to antiquity.

The Greek Histiaeus, while being held prisoner under the Persian king Darius Hystaspis, was said

to have warned his countrymen of Persia’s intent to conquer them through a secret message [24]. He

did so by shaving the head of a trusted slave, tattooing the message on his head, and then waiting

for his hair to grow back before sending him off. The slave could easily travel to the recipient as he

was carrying no messages, at least in plain view. Many other interesting techniques for hiding the

existence of information have been used since. Today, with the use of the modern computer and the

Internet, methods that use rudimentary mathematics and take advantage of a file type’s storage

format can be employed to hide data in audio, video, image, and even executable files. In [12], the

author claims to have randomly downloaded 500 JPEG images from eBay and found, using his own

analytic methods, that over 150 had secret data embedded in them.

Given the variety of file formats and communication protocols that exist today, many tools

have been developed to hide information in digital content [28], with the vast majority of them

dealing with image files. This is due to their extensive use over the Internet.

A file format that has promise of becoming widely used on the Internet is Scalable Vector

Graphics (SVG). Based on the Extensible Markup Language (XML), SVG supports the delivery

of two-dimensional graphical images with rich content for display on a variety of devices. Given

the ability to create SVG dynamically on a server, the potential for embedding information is

great. This work proposes algorithms for embedding information into SVG and implements three

different approaches, each with variations to further explore the nature of the embedding techniques.

Preliminary testing has been conducted to show the viability of SVG as a cover medium for

steganography.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. CHAPTER 2 BACKGROUND

This chapter provides an overview of both steganography and Scalable Vector Graphics

(SVG) for an understanding of the information hiding techniques presented in the next chapter.

2.1 Steganography

Steganography is a form of information hiding. Information hiding techniques may be used

for numerous reasons, although there are three major concerns being addressed today. The first is

the augmentation of data in a file for the benefit of the intended recipient, the second is the tamper-

proofing of a file to implement copyright restrictions, and the third is the concealment of sensitive

data to be sent across an open medium—as in steganography. Each information hiding technique

addresses one of these concerns. Each concern requires a different level of care, and therefore incurs

a certain level of difficulty in implementation.

The least constrained concern of the three is the augmentation of data in a file for a user’s

benefit. In this case, the file format specification can be designed to hold the information so that

a program that reads the file can understand and make use of it. An example of this can be found

in the digital storage of audio. The MP3 file format stores compressed audio data so that songs,

audio books, or other sounds may be more easily transferred over communication networks. These

files also carry extra information, not necessarily “hidden” per se, but areas where data other than the audio itself is stored. This data—the title of the audio, the composer or artist, if applicable the

album where the song can be found, etc.—augments the accompanying audio for the benefit of the

user of the file. The only real constraints are that the extra data does not significantly affect the

size of the file or the quality of the audio.

3

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 4

On the other hand, the most constrained concern requiring the application of information

hiding techniques is digital watermarking [13], which is the injection of hidden data into a file to

distinguish it from copies of itself. Since most computer users today have the ability to make digital

copies of music with practically no loss of quality, industry concerns of needing proper copyright

protections have intensified and hence fueled studies into this area. The trick is to have no (or an

undetectable) loss of quality to the file containing the watermark. Additional requirements of a

digital watermark are the need for it to resist its removal and remain intact through conversion,

compression, and transmission. If the watermark can somehow be removed, it should be at the

cost of the file degrading in quality or being rendered useless. This definitely does not lend to the

incorporation of the watermark into the file format specification, since it would more than likely not

be able to resist conversion, and could be easily found and erased or altered without consequence to

the file. Therefore, digital watermarking requires a more creative solution that can depend on the

format of the file (more powerful watermarking techniques do not), but does not include explicitly

adding anything to its storage specification.

The constraints of steganography fall in between those of augmentation and digital

watermarking. Steganography requires the same creativity as digital watermarking, but refers

to the implementation of secretive communication between two or more parties where the fact that

communication is taking place is unknown to anyone else. The idea is, as with digital watermarking,

to have no (or an undetectable) loss of quality to the file containing the secret data. Therefore,

an outside party could access the file and never know that it contained extra information. Since,

unlike a digital watermark, the secret data should not be known to exist, steganography does

not necessarily require the same level of resistance to removal. What it does require however is

resistance to methods that try to detect its use on a file.

2.1.1 Principles of Steganography

Steganography is basically the science of hiding information inside of other information

where only the intended recipient(s) know of its existence and can find it. There are three different

components to it: the information to hide, a cover object, and a stego object. The information to

hide can be anything, although most methods have an upper limit to the amount of information

that can be hidden. Hence, text messages are usually easier to hide than high-resolution images or

video. Some techniques hide only one bit of information: for example a 1 for true or a 0 for false.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 5

It may not sound like much, but take for instance the following example. Suppose a secret society

has two places of meeting, and the decision of which to use from month to month is made by the

president the week of the meeting. This is so that he can survey throughout the earlier part of the

month which place will be safest. Since this is a secret society, the president does not want to be

seen communicating this information to any of the members, nor use open lines of communication.

Therefore, he uses a third-party bulletin board or Web site that all of the members would be visiting

anyway, and embeds within a picture on that site either a 0 to meet at one place or a 1 to meet at

the other.

In a steganographic system the cover object is a vehicle for the hidden information. Today,

image files, audio files, and compressed files are all used as cover objects, although the set of

possible cover objects is not limited to these. In the use of the most common steganography tools,

the information to hide is input along with a cover object, and the output is a stego object. A

stego object is simply a cover object that has secret information hidden in it. The ultimate goal

of steganography can then be described as embedding secret information in cover objects such

that given an arbitrary set of cover objects, there is no way to tell which, if any, contain secret

information. As with anything, there are exceptions to this process. In some steganographic

algorithms there is no cover object, where only the information to hide is input to produce the

stego object. These and other more common techniques will be described in the next section.

There are three categories of steganography: pure steganography, secret key steganography,

and public key steganography. Pure steganography requires no prior exchange of information

between the two parties communicating and relies on security through obscurity. This means that

the algorithm is not publicly known, and therefore the level of testing is also unknown, making the

tool unproven. One has to go on faith alone in those involved in the tool’s creation to be assured

covert communication. Numerous instances of the false sense of security through obscurity can be

cited [37]. One particularly interesting item noted in [51] was the Secure Digital Music Initiative

(SDMI) [45]. It was set up by the major players in the music industry who were attempting to create

a robust watermarking solution. They wanted to seek public scrutiny of their algorithms by means of a contest challenging people to break various watermarks. Unfortunately, any successful contestants

had to sign a secrecy agreement to collect their prize, essentially obscuring the weaknesses of

the solution to broader research and comment. Those who refused to sign the agreement and

presented their findings elsewhere [14] were threatened with legal action. Pure steganography

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 6

is used in this research to test the viability of SVG as a cover medium, but for an effective tool to

be developed, either secret key or public key steganography should be used.

Secret key steganography usually uses a publicly known algorithm, and relies on a secret

key chosen beforehand by the two parties communicating. This key is needed to both embed and

extract the hidden information, and if the proper key is not used, it cannot be known if data is

actually hidden in a given cover object. If prior secure—or, if desired, covert—communications

cannot be conducted to share the secret key before covert communications, another possibility is

public key steganography [48] [3]. It entails the sender using the recipient’s public key to embed the

information, which can only be detected using the recipient’s private key. This is analogous to how

the public key infrastructure works in cryptography. The interesting characteristic with public key

steganography is th a t even the sender should not be able to detect the secret message in the resulting

stego object. As another alternative, [48] proposes a steganographic key exchange protocol, where

the communicating parties exchange a sequence of messages that look like normal communications,

and at the end of the sequence each party is able to compute a shared key. This shared key can

then be used for secret key steganography. No matter how it is carried out, steganography is not

useful if the existence of secret information can be proven by outside parties.

The final concept that dictates the usefulness of a steganographic algorithm is bandwidth.

Some algorithms can always hide the same amount of information, regardless of the size of the

cover file, while the embedding capacity of others depends upon the size of the cover object. We

define bandwidth as the ratio of the size of the hidden information to that of the cover object.

Suppose with a particular steganographic algorithm we are able to hide 10 kilobytes of information

in a 100 kilobyte file. This would provide a bandwidth of ^ = 10%. Similarly, embedding 25

kilobytes of information in a different 100 kilobyte file would be a bandwidth of 25%. Since some

steganographic algorithm’s embedding capacity can vary by the cover object used, we take an

average of the algorithm’s bandwidth across a number of different cover objects.

Many steganographic algorithms use extra bits for a header and for error correction, which

take up some of the embedding space and therefore reduce the amount available for the information

itself. However, these are functions of the message formation, and in this work we are only looking

at methods of embedding information and not a particular tool. Therefore, we will include all

available space in bandwidth calculations.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 7

2.1.2 Techniques

There are numerous techniques today that implement steganography. Both [29] and [51]

provide excellent surveys of the creative ideas in the field. The most common deal with using images

as cover objects. They are usually simpler to understand, and hence provide a good introduction

into this niche of information hiding.

The primary example of a method used to embed secret information in images takes

advantage of the “noise”, or least significant bits, in the image. Substituting data in the least

significant portions of an image should not affect its overall look, making the modifications

that transport the secret data unnoticeable to the casual observer. More advanced methods

use techniques from signal processing such as spread-spectrum communications to scatter secret

information over the entire cover image. This can even take place across different bit-planes in an

image [30]. To provide a basis of comparison with our embedding techniques using SVG as a cover,

one of the tools analyzed in our testing is S-Tools [9]. It hides data in image files by spreading the

bits in a random walk through the least significant bits of the colors in the image. Though not a

part of our analysis, the tool also performs a similar embedding approach with sound files.

Other advanced methods apply a transform to a complex part of the image and act

on things such as the luminescence or the color palette. These techniques take advantage of

the inability of the human eye to recognize slight variations in the colors of an image. Other

forms of image transformation provide opportunity for embedding, to include the popular Joint

Photographic Experts Group (JPEG) image format [31]. These techniques embed secret information

by manipulating the coefficients of the discrete cosine transformation (DCT) that is applied to an

image to compress it. Another steganography technique that takes advantage of a transform uses

MP3 audio files as cover objects [39]. The secret data is embedded during the compression of

the audio, altering the error-correcting coding process and hiding the information in the parity.

As a final transform domain example, GZSteg [8] provides a simple modification to the GZIP

compression algorithm to hide information. Within the tokens GZIP uses to cite repetitive

information throughout a file, GZSteg manipulates the calculated length of the repetitive data.

GZIP has no problem unzipping these stego files.

Understanding the statistical properties of the cover object and its domain (e.g., natural

images) can aid in the effectiveness of hidden information going undetected. Though earlier ideas

included modifying the statistical properties of a cover object in order to embed secret information

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. [6], today much of the focus is on maintaining the statistical properties of the cover object [19]

to increase security. The second tool analyzed for comparison with our embedding techniques,

Steghide [25], hides data in image files by taking a graph-theoretic approach and swapping pixels

to embed information while maintaining first-order statistics. Though not a part of our analysis,

the tool also performs a similar embedding approach with sound files.

Textual document files, such as those saved in Microsoft Word, also have interesting promise

as cover objects. Tweaking the spacing between lines, words, or even characters can be used to hide

information. Many of the techniques take advantage of subtle changes in formatting, and some try

to format text so that it is not visible in the user interface of the tool(s) used to view the document.

One older technique, before the days of computers, put tiny dots above relevant letters to encode

a secret message. The receiver required a magnifying glass to read the secret message. Other older

approaches involved invisible inks that required heat, water, or exposure to special light to become

visible. In digital files, white space can be “invisible ink” for a binary message. The third and final

tool analyzed for comparison with our embedding techniques, Snow [33], conceals messages in text

files by appending tabs and spaces on the end of lines.

In steganography there are also generation techniques that take a secret message and

produce a stego text from it without the need for a cover object. Not surprisingly, they usually

require user interaction to make sure that something nonsensical is not produced. One example of

this is a steganography technique that produces a text file using a context-free grammar, where the

specific productions that are used dictate the secret message [16] [50]. Another instance of this type

of tool takes a secret message and produces from it the recap of a baseball game, detailed pitch by

pitch. Similarly, a tool could generate an image using fractals, with properties set by the secret

data to embed [15].

Scalable Vector Graphics (SVG) files are stored as Unicode text that is not formatted, so

the techniques presented that manipulate formats in textual files would not work. This moves the

focus to the aspects of how Scalable Vector Graphics files describe the text, graphics, and animation

that are rendered. The next chapter will cover the techniques reviewed for embedding information

into SVG files.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 9

2.1.3 Steganalysis

In order to have any confidence that hidden information is actually safe from detection,

proper scrutiny of the algorithm used is required. This includes, but is not limited to, having

experts—aside from the steganography algorithm’s creators—work to detect any characteristics in

stego objects produced by the algorithm that point to steganographic modifications. Steganalysis is

the science of discovering, decoding, and/or destroying messages hidden in a cover file. Just being

able to find out that messages are being hidden is a feat. Efforts have been made to detect the

signature of some of the more popular steganography algorithms, but automated scanning tools are

not mature. Here we cite some of the im portant work in the field.

Looking at algorithms that hide information in the least significant bits of images, [20] have

found a spatial correlation in typical cover images where the least significant bit (LSB) plane can be

predicted to some degree based upon the other more significant bit-planes in the image. This allows

for the detection of subtle modifications amongst the least significant bits of an image. We can also

look at how an image is quantized using JPEG compression to detect the use of steganography in

the spatial domain [21]. Algorithms that look for (or create) areas of similarity in order to exploit

slight differences to hide information—such as manipulating the color table of a GIF image—are

also susceptible to statistical analysis [52]. Reviewing pairs of values in the histogram of color

frequencies within the color table of a GIF can show the use of this type of steganography.

As with Steghide, [41] developed an approach to hide information while maintaining first-

order statistics. But in using the novel approach of embedding the maximum possible amount of

information again in the suspect image using the same algorithm, [22] were able to gather a measure

to detect this use of steganography. In measuring the difference in spatial discontinuities at the

boundaries of all 8x8 blocks in the suspect image before and after the test embed, it measured

significantly less if information was already embedded in the suspect image than if it previously

contained no hidden information. The idea is that any previously embedded information already

had an effect on this test statistic, which therefore also allows for an estimate as to the length of

the hidden m essage.

Research has also been done into tools that focus not on specific steganography algorithms,

but on patterns that normally occur in digital images [53], so that potential stego images can

be identified. These are termed blind detection techniques, as they do not have prior knowledge

of any particular algorithm, and could be used to detect a range of steganography techniques. In

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 10

taking a general mathematical approach, [11] have been able to detect and extract information from

numerous techniques, including those using spread-spectrum steganography. One of the most cited

blind detection approaches uses a decomposition of images [17] [35]. This method, which

uses higher-order models and counters embedding methods that fix first-order statistics, requires

training on many cover and stego images in order to properly work.

Looking beyond the use of any given algorithm, there are some simple things that can be

done to discover the use of steganography. A lot of useful information can be extrapolated from

the patterns of e-mail users. If the same large image is consistently being sent between a pair of

users, this can signal the use of steganography. This is especially true if the image does not seem

to change, but the date/time stamp on the file does. Another giveaway, which some steganography

algorithms will alter (but should not), is the size of the cover file. Even if the size of the cover file

does not change, someone could take a cover object and its corresponding stego object (or even

two stego objects that use the same cover), compress them, and measure if there is a considerable

difference between the compression ratio on the two objects. Minor differences may be explained

by transmission errors or other subtle anomalies, but major differences can point to information

hiding.

To some extent this sort of analysis can be countered by using a different cover object

each time or through the posting of stego objects to Web sites instead of attempting direct secret

communication. But any abnormal behavior can throw up red flags. Taking our example of a secret

society in Section 2.1.1, if the members only visit the Web site or bulletin board containing the

stego image the week of the meeting and only then, the pattern of behavior could be detected. The

main point to be made is that the use of a reasonably undetectable steganography algorithm alone

does not ensure undetectable communications.

In taking a broader approach to find any use of steganography on the Internet, [42] wrote a

tool to crawl the USENET archives and eBay using a depth-first search. Every JPEG image found

was processed using some of the statistical steganalysis techniques previously presented. Testing

conducted before the search proved the ability to detect use of three popular steganography tools

that use JPEG images as cover objects—JPHide[34], JSteg[31], and Outguess[40]. Their search of

two million images provided just over 3% as positives, but the payload in none of these images could

be extracted using a dictionary attack, leading the authors to believe them false positives. This

contradicts the claims of [12] cited in the introduction, and continues the debate of the extent of

real-world use of steganography. Given that the USENET archives and eBay, though large, do not

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 11

encompass the entire Internet, a different approach has been proposed to only search information

that is requested [7]. By monitoring HTTP traffic, sites that are not otherwise linked to can be

scanned, but dealing with the breadth of information gathered then becomes a challenge.

As with cryptography, in steganography there is a constant battle between those hiding and

those attempting to detect and extract. With steganalysis there is another option: to destroy any

possible hidden message rather than search for its existence. As far as images go, image processing

techniques such as smoothing, compressing, or transforming usually destroy any hidden data. These

are referred to as active attacks on a steganographic system, which are covered in the next section.

2.1.4 Attacks

Attacks are considered actions that run counter to what the steganographic system is

trying to do: hide the existence of secret data in a cover object. There are three kinds of attacks

on steganographic systems: passive, active, and malicious. The study of these is a major part of

steganalysis, with each approach having different goals.

Passive attacks consist of simply looking for the existence of hidden messages. As covered

in the previous section, statistical analysis can be done to detect modifications made by some

steganographic systems. This can include not only detecting the presence of hidden data, but

also attempting to gather as much information as possible about the embedding. Specifically, the

steganography algorithm used, any parameters of the algorithm, where the information is hidden in

the cover object, the estimated length of the message, and the key used are all sought. Overall, these

type of attacks seek to gain as much information as possible, with the ultimate being extraction of

any hidden messages. In this work we conduct passive attacks aimed at detection of the embedding

techniques devised to assess Scalable Vector Graphics files as a viable cover medium.

Active attacks consist of making changes to a cover object between the sender and

recipient (s) of a message. Steganographic systems are usually sensitive to cover modifications.

When images are used as cover objects, they are susceptible to image processing techniques such as

smoothing, filtering, and transformation to alternative formats. Active attacks are not concerned

with finding hidden data, rather they focus on destroying any potential hidden information in

a cover object while maintaining an acceptable level of fidelity in the cover itself. Combating

steganography in network protocols, [18] defines “minimal requisite fidelity” as the threshold where

overt communications can take place but no other information is preserved. Unused bits are reset,

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 12

IDs are manipulated where possible, and any detected semantic anomalies (e.g., unknown source

and destination ports in a TCP packet) are adjusted. An example of an effective active attack

on a steganographic system that uses the least significant bits in an image file would be to just

randomize all of them. As with the LSB embedding technique, the attack would not harm the cover

object since these bits contribute the least to the image display.

The third and final type of attack is the malicious attack. This is when someone forges

a stego object or initiates communication with another using a steganography tool under a false

identity. If secret key or public key steganography are not used, then this type of attack can be a

real problem. Without distinguishing information such as a secret key, it is hard for the recipient

of a stego object to verify the identity of the sender, especially if it is something downloaded from

a common Website.

2.1.5 Security and Robustness

Security and robustness are two important aspects of any steganographic system. The

security of a steganographic system is defined as the difficulty in detecting data hidden within a

stego object produced by the system. In the next chapter we define a set of visual and statistical

tests that will be used to compare different embedding method’s resistance to detection.

In addition to the detectability of the use of a particular embedding method, when a

complete steganographic system is being developed there are actually four other requirements that

should be implemented to make the system more secure. First, secret data should be encrypted and

embedded using a public algorithm and secret key. This way, a user can understand the limits of the

system, and still keep their data safe. The second requirement is that only the holder of the correct

key can detect, extract, and hence prove the existence of any hidden message in a cover object.

Outside parties should not even be able to find statistical evidence that hidden data exists. Third,

even if the contents of one piece of hidden data is known, there should be no chance of detecting

others. And finally, it should be computationally infeasible to detect hidden information. Just as with cryptography, any method that could break the system should not be worth the computing

power needed.

A steganographic system is considered robust if the embedded information cannot be altered

without making drastic changes to the stego object. This is difficult to do, and steganographic

systems can usually only focus on being robust against a very specific set of mappings or attacks

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 13

(e.g., conversions to a certain format). If desired, the message itself can be more robust by having

error correcting codes inserted under encryption. Redundancy can also be employed to help ensure

the survival of hidden data, or it can actually be placed in perceptually more significant parts of

the image, using embedding rules that operate within some transform domain. It is important

to point out though that robustness is not enough against a malicious attacker, and there is a

tradeoff between robustness and security. The more robust a steganographic system is made, the

more structured the information becomes, making it easier to detect through statistical passive

attacks. Also with an increase in robustness comes the need for more bits, hence decreasing the

useful bandwidth of the system.

2.2 Scalable Vector Graphics

2.2.1 Raster Images

The most common graphics on the Internet are considered raster images because they

are defined as a matrix of pixels, each with its own defined color. The quality of the these raster

images is expressed in dots per inch (dpi) where the higher the dpi, the better the image quality. Not

surprisingly, another side effect that comes with an increase in quality is an increase in the resulting

image’s file size. Bitmap files follow this format precisely, defining the location of each pixel and

its color, but the most common files of these type found on the Internet are Graphics Interchange

Format (GIF) and Joint Photographic Experts Group (JPEG) images. For efficient delivery of

content over the Internet, a balance must be struck between quality and file size. The reason why

GIF and JPEG images are so popular on the Internet is because they each take approaches to

reduce the size of the file for quicker transmission while maintaining a level of quality higher than

a normal bitmap image of equivalent file size.

One weakness of the raster formats though comes in magnification. This may occur if the

image is of a map and a closer view of a section of streets is desired, or if an image needs to be

clipped and blown up to the size of the original image. As raster images are blown up, smooth lines will become jagged, and the image will be seen for what it truly is, a set of blocks of color.

The higher the level of zoom, the bigger the blocks. How quickly a raster image falls into this state

depends on the quality in which the image is stored. Therefore, the compromise between quality

and file size not only has to address transmission speed, but also potential magnification. One

approach to this problem, currently used by MapQuest [36], is to have multiple files of the same

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 14

image area—a map in their case—stored at different magnifications. This allows them to provide a

small number of magnifications, and the time it takes for a new magnification to be shown becomes

dependent upon transmission speed.

Also with raster images, a resolution problem comes up when we look at the various devices

upon which Internet content is being delivered. Most common are personal computers and laptops

with monitors that provide multiple resolution settings. Depending on the viewable size of the

monitor, typically a resolution is set so that text can be easily read. Images must be sized to the

lowest common monitor resolution in order for them to be seen in their entirety on a Web page. This

type of sizing deals with the number of pixels the image uses on the content delivery system (e.g.,

the monitor) and, though related to file size, is not the same thing. Standard monitor resolutions

include 640x480, 800x600, 1024x768, and 1280x1024, all measured in pixels. Newer models, to

include wide screen displays, offer even more options. Many sites on the Internet assume monitors

to be set at 800x600 and are hence best viewed at that resolution. This means that images on these

sites will potentially not fit on monitors with a smaller resolution, wasting the transmission time

of sending a larger image. Also, for those with a larger resolution monitor who want to magnify

images—maybe to stretch them for a screen wallpaper background—quality becomes a concern.

This headache of trying to design to encompass most monitor resolutions is magnified today by the

addition of various handheld devices that can receive rich Internet content. These include Personal

Digital Assistants (PDAs), Pocket PCs, and even cellular phones. Some of these devices today are

achieving resolutions of 320x240 and even 640x480.

2.2.2 Vector Images

Vector images, unlike their raster counterparts, are defined by objects rather than pixels.

Each object in a vector image is composed of a collection of points connected by lines and curves to

define its shape, while color can be specified once each for its area and boundary. Compare this to

a bitmap image where each and every pixel has a defined color. The way that vector images achieve

this level of simplicity in definition is through their use of software for rendering. The definitions

of vector objects translate into instructions for a software engine to draw the image on a particular

output device. This way, a broad range of output devices can be supported, and an image will

not lose quality (i.e., resolution is not a factor) in its display on any given device. It then follows

that magnification of a vector image will not produce the blocking found in raster images, since

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 15

the image is redrawn to show a particular level of detail. Also, when printing vector images the

maximum output resolution of the printer can be used.

Simple shapes can be defined with ease in vector images. For example, a circle can be

defined by the coordinates for its center along with the measurement of its radius. This is the

minimal amount of information needed to create a circle. Similarly, drawing a rectangle only

requires the coordinates of one of its corners along with its height and width. This saves on the

size of a vector image file, making them ideal for the delivery graphical content over the Internet.

Hence, the balance of quality and file size demanded by raster images is not required by vector

images.

Vector images master the weaknesses identified in raster formats, but they certainly have

some weaknesses of their own. The one that initially stands out is the requirement for a specialized

piece of software to render any vector image. More specifically, there is the reliance on the ability of

this software to render images efficiently. The more complex an image is, the more difficult and time

consuming it is to render, making vector images more dependent on the CPU than raster images.

The upside is that this should improve with time as software engines improve and processor speeds

increase. Also, most machines today have Graphical Processing Units (GPUs) specifically for visual

rendering. Some common vector image file types are Encapsulated PostScript (EPS), CorelDraw,

AutoCAD, (WMF), Macromedia Flash, and Scalable Vector Graphics (SVG).

2.2.3 The Scalable Vector Graphics (SVG) Format

Scalable Vector Graphics (SVG) is the first open vector graphics standard for the Web,

intended for producing two-dimensional vector graphics where zooming and panning are possible

with no loss of quality to the rendered image. It became a World Wide Web Consortium (W3C)

Recommendation in the latter half of 2001, with the latest revision coming in 2003 [43] along with

a mobile version [44]. It is an Extensible Markup Language (XML) based graphics standard, so

it integrates well with other XML Document Type Definitions and can make use of Extensible

Stylesheet Language Transformations (XSLT) for visually representing many types of data. While

there is native browser support for the language in only a few packages (Mozilla, Konqueror, etc.),

both Adobe and Corel have SVG viewers that both plug into the Internet Explorer and Netscape

browsers.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 16

Scalable Vector Graphics files can contain syntax to display vector graphics shapes, text,

and even raster images. Objects in an SVG image can be grouped, styled, and transformed.

Gradients, patterns, masks, filter effects, and simple alpha blending are available to provide rich

graphical content. In addition, animation elements are defined in the recommendation and SVG

images can be designed to be interactive with scripting. Since it is a vector graphics format, SVG

is resolution independent allowing images to be rendered relative to the size of a browser window.

This all comes together to provide a powerful toolset for the creation and display of two-dimensional

graphics.

The format is popular with cartographers as a simple, open solution to delivering interactive

maps over the Web [38], as opposed to a proprietary format with limited interface capabilities and

limited extensibility. For developers there are many tools available that can allow for easy viewing

and editing of SVG due to its roots in XML. Also, any programming language that can manipulate

text files—or better yet, parse XML—can generate and manipulate SVG. In the open source realm

there is also the Apache Batik project [4]. It is a Java-based toolkit providing a set of code modules

for the generation, manipulation, and even viewing of SVG images. Using these modules allows

developers to more easily integrate SVG into their client as well as server-side Java applications.

Nuts and Bolts of SVG

This section is an introduction to the basic ideas of SVG to provide a context for the

information embedding techniques described in the next chapter. Therefore, this is not by any

means as complete as a text based solely on SVG. For a more in-depth treatment of the material, see

books such as [49] and [10], and of course the World Wide Web Consortium (W3C) recommendation

[43]. A simple example is provided in Figure 2.1 with its related source in Listing 2.1 to explain

some of the features of the SVG markup.

In our example, the svg element is the document element, with all its children (i.e., elements

contained within it) describing the image to be rendered. The width and height attributes define

the extent of the SVG viewport, which is the area where content is rendered. In our example, the SVG content covers the entire page. The viewBox attribute scales the coordinate space within

the viewport, with the first two digits defining the (x, y) coordinate of the top-left corner, and

the second pair defining the bottom-right corner. Therefore, in viewing this SVG document on

a monitor with resolution greater than 640x480, each unit in the coordinate system covers more

than one pixel, making the image appear larger. Similarly, at lesser resolutions the image will

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 17

svg_8xair|ile.svq

i p c t .______r r r g »2 ^

Figure 2.1. Hello World SVG Example Image

appear smaller. It is possible for another svg element to be nested within the document element,

with its own coordinate space defined, to allow for the simple movement and independent sizing

of portions of an image. If conflicts of scale occur, there is a preserveAspectRatio attribute to

provide direction to the SVG rendering engine as to how to preserve aspect ratios.

Aside from the units of the coordinate system, other units of measure can be specified to

size and position graphics and text. In the example, the width and height of the viewport are

specified as a percentage of the width and height of the browser window. Items can also be sized

relative to the default font size. The absolute measurements available for use are pixels, points (72

per inch), picas (6 per inch), centimeters, millimeters, or inches.

The other major piece of overall presentation of SVG content is defined through styles.

Individual elements of style, such as stroke and fill color, are attributes on SVG graphical elements. Cascading Style Sheet (CSS) declarations are used in the style attribute, as presented in the

example. Our example also includes a style element, where internally an entire CSS style sheet can

be specified, though here it is used only to define the default font. As with other XML documents,

SVG can also be styled by referencing an externally defined CSS style sheet. Most of the SVG

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 18

documents we test for embedding hidden information (see appendix A) define styles in yet another

way: by adding entities to the document type definition.

Listing 2.1. Hello World SVG Example Source

< g transform= “translate(100,100)” >

< path id= “scribble” d= “M 95 150 C 103 103 230 25 214 122 C 208 164 191 238 228 226 C 284 208 376 35 367 204” style = “ fill :none;stroke:rgb (0,0,0); stroke—width:2”/>

Hello World! < /tex t>

< /g > < /svg>

The example’s internal style sheet definition is contained within a defs element. Usually

placed early in the document, this element serves to hold common definitions within the SVG

document and hence reduces any unneeded redundancy. Other than common styling, elements that

define gradients, filters, and other visual effects can be placed here for use. Also, text and shapes

that are repetitively used within the SVG document can be placed here and simply referenced where

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 19

they need to be rendered. And finally, SVG provides the pattern element, defined within the defs

element, as a way to create repeating visual patterns for the fill of a graphical object.

Basic elements and shapes are easily defined in SVG. A rectangle is drawn using the rect

element, defined by the positioning of its top-left corner and its width and height. Just as simple,

a circle is defined by the positioning of its center and its radius. Similarly, SVG provides elements

to define a straight line between two points, jagged lines containing more than two points, closed

shapes having three or more edges, and ellipses. Even raster images can be inserted amongst

rendered SVG content using the image element.

It is important to note the order in which the elements in the example are defined. In SVG,

objects are drawn in the order they appear in the document. Hence, later elements are drawn over

previous elements existing in the same drawing space. It is possible that opacity can be adjusted

so that the color of an earlier drawn element shines through and is mixed with that of the latter.

This overall concept is referred to as the painter’s model in SVG. In our example, the rectangle

provides the background for the rest of the drawing. If it were to be defined later in the document,

everything defined previously in the space it covers would not be visible.

The SVG path element allows for the creation of arbitrary two-dimensional graphics. It

supports the mixed use of straight and curved lines, along with the ability to move the drawing

instrument without drawing in the viewport. Most items exported out of a graphics package such

as Adobe Illustrator to SVG make extensive use of these elements due to their flexibility. All of

the data required to draw the object is contained within the d attribute. Our example provides

all positioning in absolute coordinates, but it is also possible to provide relative values. The initial

“M” moves the drawing instrument to position (95,150) in the viewport. It then draws three cubic

bezier curves put together to form what looks like the doodle of a pen. Each curve is defined by tw o

control points and an end point, and starts from the previous positioning of the drawing instrument.

Also available for use, but not shown in the example, are straight lines, quadratic bezier curves,

elliptical arcs, and the ability to close the shape being drawn.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 20

Unlike raster image formats, text that is shown within an SVG image can be selected. It is

stored explicitly within the SVG document so that it can also be searched. In our example, the style

of the text is defined in the CSS style sheet within the defs element, with an override specifying

letter spacing in the text element. The textP ath element places the text along a path, specifying

the ID of the path to use. Within SVG the XML linking language, XLink, is used for all internal

and external referencing.

Instead of nesting another svg element within an SVG document in order to group portions

of the image for positioning and styling, an alternative is to use the g element. As with the defs

element, it can make the SVG document smaller in file size by providing a group of elements with

one point of reference, one style, and even one transformation. The transform attribute enables

the mass movement of a group of elements. In our example, all of the elements of the image are

moved towards the center of the viewport through a translation, which is an explicit coordinate

adjustment. Other transforms available are scaling, rotating, skewing, and the ability to combine

any or all of them.

S u m m ary

Given that it is XML-based, SVG is stored as Unicode text and requires adherence to the

strict syntactical rules of XML. The open nature of the format provides that text being displayed

by SVG in any way can be selected, making it more accessible than in other graphics formats. This

is especially important for search engines that are XML-aware and even for people with disabilities

using special text readers to browse Web content. Another benefit to SVG being text-based is that

people can understand how others have created images in SVG by looking at the source, hence

accelerating its adoption (as with HTML). Using HTTP 1.1, the source can be compressed, leaving

a small footprint on a server. Therefore, SVG files can be dynamically generated and edited

by common programming and scripting languages on a server, delivering up-to-date lightweight

graphically-rich content over the Internet. Examples include an interactive map with the latest

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 21

temperatures and storm tracking, or a stock ticker with an animated bar chart displaying the rise

and fall of a users’ favorite stocks.

This discussion on SVG would not be complete without a reflection upon the weaknesses

of the format. The initial problem that stands out is that for the two Web browsers used most,

Internet Explorer and Netscape, support for SVG is not built in. Though there are plug-ins to

support the format, there is also a more widely used—but proprietary—vector graphics package

in Macromedia Flash. In line with other vector graphics formats, another current weakness of

Scalable Vector Graphics is its need for extensive CPU power to perform drawing and filter effects

on a client machine. In addition, though the document structure and SVG schema elements are

simple to comprehend, when put together to draw a complex image the resulting source file loses

some of the benefits of readability. This can be especially true when using the powerful path

element to draw complex objects.

Overall, given increases in CPU speed along with improved implementations from

established firms such as Adobe, its open standard, its portability, and the embrace of its use

amongst cartographers, SVG has a good chance of being embraced by wider audiences.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. CHAPTER 3

COVERT CHANNELS IN SVG

In this chapter we survey different methods of embedding information into a Scalable Vector

Graphics file, with the focus on finding a covert channel that will be useful for steganography.

Of the potential methods reviewed, three of the ideas were implemented and tested, each with

variations implemented for further exploration. In order to provide a basis for comparison, three

currently available steganography tools were also tested. The usefulness of each method is measured

through bandwidth and detectability. Better methods will allow for higher bandwidth and lower

detectability.

In scoping the experiment, a set of assumptions has been made. First and foremost, we

assume that our payload is encrypted. This way, even if the secret message is found—breaking the

steganography channel—it cannot be easily read. Another advantage that the encryption brings

is that it will randomize the data to make it look more like noise in the cover work. All of our

implementations simulate this by injecting a pseudo-random stream of data as the secret message.

Our second assumption has to do with steganalysis and resistance to attacks meant to detect and

sometimes crack steganographic media. In this work we address defense against passive attacks,

meant to detect the use of steganography on a cover work and possibly attempt to extract any

hidden information. Therefore, we are not going to further discuss active or malicious attacks,

22

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 23

where the content of the cover work or secret message is changed. It is assumed that the encryption

technique used should resist message substitution.

An example of only having to worry about passive attacks can occur when the SVG file

used as the cover work is on a secure server as a read-only file. The communication path is setup

beforehand where the SVG document is part of a Web site that is hit by many people so that those

who are downloading content for the hidden information do not stand out. SVG is not meant to be

a write-once format, but rather a way to deliver dynamic graphical content, so consistent updates

to the file—to put in new hidden information—should not stand out to someone looking for the use

of steganography.

3.1 Embedding Methods

In searching for related research, everything found dealt with digital watermarking

techniques, meant to only store enough information for a watermark while remaining robust to

attempts at removal. The most relevant of these is the development of a watermarking scheme

for SVG images in [5]. The method slightly modifies vertex coordinates to embed information in

the magnitude of a polygon’s Fourier descriptors, also using them as a test statistic to verify the

watermark’s existence.

Other research has looked at numeric sets [47] and moved on to the watermarking of

relational data [1], Here also values are manipulated within a certain threshold so that when a

statistical test is performed on the data with the right parameters, the watermark can be proven

to exist. A graph-theoretic approach is taken in [46] to watermark XML data, looking at both

the content and the structure of the data. In [23] the focus moves to watermarking the results of

queries from relational data and XML documents.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 24

3.1.1 Insertion

One of the most obvious methods of hiding information in a cover file is to simply insert it

into the file. The trick of insertion is to not affect any of the characteristics of the cover file where

someone (or something) will be looking. Hence, many of the methods placed in the category of

insertion fall in the realm of security through obscurity. This means that their security relies in

part on the premise that the channel where information is being inserted is obscure enough to be

covert (i.e., no one will think to look there). This can certainly work, but it poses an incredible

security risk. The moment someone finds out the method of embedding—hence uncovering the

covert channel—the hidden information can be easily extracted. Good encryption can at least keep

the details of a message secret, but once the covert channel is compromised it is known that two

parties are communicating, completely defeating what steganography is meant to protect.

In this section we explore methods of insertion for hiding information in SVG files. The

main challenge in this (and any steganographic method for that matter) is that the source is stored

as text. Therefore, extra efforts must be made to not only hide the presence of extra information

in the rendered file, but also in the source text.

Inserting W hite Space

The first method devised for embedding information in SVG files is the addition of white

space to the source. By definition in the SVG Specification [43], white space includes any spaces,

tabs, line feeds, carriage returns, and form-feeds. With the exception of the character data of text

content elements (e.g., text, tspan, etc.) where it can be optionally preserved, in other content it

is consolidated and in many cases removed. Therefore, in most places where we add extra white

space, the SVG parser and rendered output will not be affected. This leaves us to focus on making

sure that the presence of hidden information cannot be easily detected through a scan of the source.

We first assume, as with most application-generated and quality developer-composed SVG files,

that each SVG markup tag is on its own line, so that the source is more readable. In regards to tags,

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 25

an SVG element can be composed of either one tag (e.g., ) or a

start and end tag (e.g., .. .), so that it can contain other elements.

One option is to add white space at the end of each line. An algorithm of this sort, using an

HTML file as the cover work, is explored in [12]. It places a number (five by default) of white space

characters at the end of each line, using a space for a 0 bit and a tab for a 1 bit. The assumption

is that a lot of HTML source is not formatted very well, and extra white space will not look out

of place. This algorithm can certainly be applied to SVG files, but when well-formatted, a simple

highlight of the source shows the variant amount of extra space at the end of each line.

To get around being discovered by a highlight, we devise another method where white space

is inserted at the beginning of each line, following a proper method of indent. Using this method,

to any casual observer the source file appears as well-formatted and readable SVG. In keeping with

a proper indent though, the bandwidth of it has an upper bound that the aforementioned HTML

trailing space algorithm does not, since too much white space at the beginning of a line would

certainly look out of place. The implementation of this algorithm is covered in Section 3.2.1 and

its bandwidth and detectability are investigated in Section 3.3.

Inserting Elements

Another possibility for inserting hidden information would be to use a generation technique

to create extra elements in the SVG document. Using a context-free grammar, productions

could map hidden data to comments or extraneous elements contained within a defs element.

Unfortunately, a machine could scan for all un-referenced elements within a defs element, defeating

the latter approach. But in the generation of comments, it would be hard for a machine to

understand what is extraneous. Even a human reader could potentially be tricked if heuristics

were in place to make them reasonably applicable to the cover file. In either case, the addition of

elements would certainly add to the file size, and would therefore have to be controlled, limiting

bandwidth.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 26

Inserting SVG Objects

Going beyond the insertion of elements, it is possible to insert and hide entire graphical

objects within an SVG cover file. This can be done in a few ways. The first and simplest way would

be to set the v isib ility attribute on the extraneous object to “hidden.” The results of a simple

scan of all objects with this setting could not be assumed to be hidden information, as scripting or

animation in the SVG document could be used to adjust their visibility. But a more sophisticated

scan could defeat this, looking for hidden objects that are not referenced in any animations or

scripts.

Another way to insert an extra hidden SVG object would be to have it drawn small enough

to not be visible at the highest level of zoom that any of the standard SVG viewers support. On

the other hand, there really is no limit to the zoom level that can be attained with an SVG image.

This is because a script can be written to transform the size of all objects to any desired level. Also,

the SVG source could be scanned for objects that could not possibly be visible in the viewport at

the highest available zoom level. Again, any scripts or animation would also have to be searched.

The final way to insert an extra hidden SVG object would be to take advantage of the SVG

painter’s model and have the inserted object drawn under another graphical object. The inserted

object would exist inside the area of a cover file graphical object, being specified in the source

before it, so that the inserted object is drawn completely over by the cover file graphical object.

This way, regardless of the level of zoom, the inserted object is not visible in the rendered image

content. Even though the item is hidden from view in the SVG image, it would not be marked as

such in the SVG source. It would take analysis of the placement of all SVG objects to find extra

objects that have no possible way of being shown. As with the other methods, this analysis would

also have to include looking at scripting and animation to ensure the object’s properties are not

changed at any time to make it visible.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 27

Inserting Vertices

A lot of data can reside in the path element, making it hard for the casual observer to

find extraneous information. Using a cover SVG file that contains path elements drawing many

straight lines, points could be added along these lines to hide information. The idea would be to

use the first point as a reference and then vary the distance between the inserted points to encode

the message. Given the measured distance m between the initial point and the first inserted point,

the reference distance r, along with a threshold value e, various values can be embedded with the

following interpretation:

m = r =$> 0

m = r + e => 1

m — r + 2c =*> 2

m — r + 3e =£■ 3

The amount of information that can be embedded is dependent upon the sizes of r and e

as well as the available length provided in the vertices of the cover object. As with any of these

insertion techniques, the additional points would add to the file size, but not necessarily as much as

inserting entire elements or SVG objects to hide information. Though more difficult for a human

reader to find in a large SVG document, a scanning program could be written to look for the pattern

of useless points.

3.1.2 Substitution

The second general category of embedding methods are those that substitute SVG source

information with equivalent syntax that has the same semantics. Similar to insertion techniques,

these methods require obscurity to effectively operate, and therefore come with the same security

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 28

risks. The improvement that these methods bring over insertion is that they provide the potential

to not increase the size of the of the file, which would certainly point to extra information being

hidden.

Substituting End-of-line Characters

A simple example of substitution could be to alternate between the different operating

system character definitions of the end of a line in the SVG source. A line feed character, as used

in UNIX, could represent 0 and the combination of a carriage return and line feed, as used in

Windows, could represent 1. Overall this method would provide a relatively low bandwidth, and

though many text editors can handle seeing both types of end-of-line definitions, some will display

the control characters and not end the line. The SVG source would look funny with some lines

ending and others not.

Using Alternative SVG Syntax

Another, potentially more subtle, approach is to take advantage of the alternative means of

writing SVG syntax in the source file. The SVG specification allows for numerous shortcuts in order

to help reduce the size of a file. For instance, in the p ath element alone there are several options for

specifying the syntax of path data. Horizontal and vertical lines from a point can be specified with

an “H” or “V” instruction, respectively, along with one coordinate instead of an (x,y) pair, since in

each case one of the values remains constant. Also, commas are optional in coordinate listings so

long as there is white space separating numerical values. And space does not have to exist between

instruction characters (e.g., ‘C’ for “curve to”) and coordinates. Another syntactical variation is

available with the specification of either absolute or relative path coordinates. Instructions in lower

case specify relative coordinates while their capitalized counterparts signal absolutes.

While some of these changes decrease readability of the SVG source, a substitution

embedding algorithm could actually decrease the size of the file, depending on the cover file. But,

alternating between different syntactical patterns would certainly be able to be found by a machine,

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 29

and even a human observer would be able to decipher that something was out of place. Making

these changes less visible and spreading them around the file would decrease bandwidth.

Breaking Paths into Sub-paths

Another possibility for substitution would be to break paths down into smaller sub-paths.

Each sub-path element would carry a piece of the embedded information, which could be the

number of points or coordinates listed within it. Therefore, the SVG image would be no different

while the source would have minor alterations. Care would have to be taken in the embedding

process so that large variations in the textual length of path elements do not occur. One change to

lower detectability would be to instead embed information using the variation in point or coordinate

counts between consecutive paths. This could serve to make the paths closer in textual length when

viewed in the SVG source.

In either of these cases though, as with all of these substitution methods, bandwidth is an

issue. While file sizes are not changing as dramatically as with insertion methods, there appear to

be bandwidth limitations in maintaining this requirement while keeping detectability low. This is

a common theme in steganography: the more information packed into a cover file the more likely

it is to be detected.

3.1.3 Tweaking

In tweaking we are still substituting to insert information, but instead of altering the syntax

of the logical structure we are altering the semantics of the source SVG file and therefore the look

of the image. The goal is to maximize the bandwidth for hiding information while keeping the

changes slight enough so that they are not visually apparent in the rendering. This category of

methods is more powerful than those in insertion or substitution due to the semantic nature of the

changes. Since the information being adjusted is responsible for the rendering of the image, without

the original cover object present it is difficult to tell where adjustments were made. Tweaking is

also referred to as bit-twiddling or embedding information in the noise of a cover object. In this

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 30

context, “noise” is considered to be extra information in the SVG file rendered below the threshold

of human perception.

Adjusting Least Significant Figures

A perfect example of noise in an SVG file can be found in the least significant digits of

some of the coordinates used to draw shapes in SVG. Someone may be able to tell the difference

between a line starting at (120,40) and one starting at (120,45), but what about the difference

between (120.234, 40.015) and (120.238, 40.010)? Is there really a need for that much precision?

Many types of files contain information at a level of detail deeper than our perception, allowing for

us to either take it away to make the file smaller (compression) or use it to hide information. The

reason why some file formats keep such a depth of information is because in some instances there

is a reduction in quality—though usually acceptable—when this information is adjusted or taken

away.

The interesting point with SVG is that in theory you can zoom to any level of detail on

an image, creating an interesting challenge in the exploration of the bandwidth limitations of this

technique with regard to detectability. Figuring out the threshold where numbers can be adjusted

as well as how to adjust them are the concerns addressed in our implementation. We devise a set

of algorithms to aid in the analysis of this technique that will be flexible enough to test its limits.

Exploring the variables of this technique—the criteria for tweaking, the digit(s) to tweak, and the

embedding method—in relation to its bandwidth and detectability will be done in Section 3.3. The

implementation of the various algorithms used is discussed in Section 3.2.2.

Reordering Information

With endless “Top Ten” lists and rankings of all sorts—in sports, movies, and vacation

spots (to name a few)—one does not have to look far to see the importance we can attach to order.

In steganography we can take a list of items where the order is not focused on and use it to hide

information. For example, take a playlist of music from an MP3 player. Today it’s certainly not out

of the ordinary for someone to be carrying around 50 songs, shuffled and played in a random order.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 31

Those 50 songs can be ordered in 3 x 1064 different ways, making for a vast number of possibilities

and allowing us to hide about 27 bytes of data. In general, a list of n items can be ordered in n!

different ways allowing us to hide about log2 (nl) bits of data.

The bandwidth of this method can be impressive depending on a couple of factors: the size

of the list being ordered and the space required to store each item in it. If our playlist is simply a

set of one byte pointers, then it would take up 50 bytes making for a bandwidth of about = 54%

for hiding information. A program called Gifshuffle [32] implements this method using the ordering

of the color map in a GIF file. Since each color requires two bytes a piece, then an image with

50 colors would have a bandwidth half that of our MP3 playlist example: 27%. To get the same

bandwidth as the playlist, the GIF image would have to contain about 1,100 colors in its color map,

and that’s without considering the storage required for the image itself. This provides good insight

into our bandwidth capacity in relation to these two factors. The space required to store each item

in the list certainly affects the bandwidth much more than the number of items in it.

Moving to SVG files, in some cases the ordering of the elements in the document does not

matter. The location of content is specified in an element’s attributes either directly, relative to

other coordinates specified in the attribute, or relative to its parent element. This way, if sibling

elements are re-ordered in the SVG file, their ( x , y ) location will not be affected. On the other hand,

elements are painted in the order in which they appear in the source, due to the SVG painter’s

model. Hence, elements that overlap in the image can be affected by reordering. Add this to the

knowledge that each SVG element is character data—with each character represented by a byte

(two if the encoding is UTF-16)—and we’re left to think that the bandwidth of this method will

not be very good at all.

Since the space required to store each item in a list for reordering seems to make or break

the bandwidth of a steganographic algorithm using it, we could look at just reordering the attributes

on each element in the SVG file. Not only does each attribute take up less space—since multiple

attributes contribute to the size of one element—but the reordering of them does not affect the

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 32

SVG painter’s model. Both the reordering of SVG elements and attributes is investigated, with

implementations described in Section 3.2.3 and the results of testing in Section 3.3.

3.1.4 Other Embedding Approaches

There are other methods that can be explored for hiding information in SVG content that

are outside the scope of this research. In order to reduce file size for more efficient delivery over the

Internet, SVG files can be zipped. There are embedding methods already developed that could be

used in this compression process [8], although a re-compression and comparison could detect the

use of this method. Also, given that raster images can be referenced and inserted into SVG images,

this opens up the possibility for exploiting information hiding techniques within them. This could

also include techniques spanning multiple images brought together in one SVG document.

3.2 Implementation

Three of the methods described in Section 3.1 have been implemented, each with variations

to further explore the nature of the embedding techniques. The source code and related descriptions

are included in Appendix B, which is organized in a similar fashion to this section. The algorithms

devised for each of the embedding techniques follow.

3.2.1 Inserting White Space

The first algorithm that creates an SVG stego object by inserting leading white space,

alg_LeadTab, parses an SVG cover object so that each markup tag can be placed on its own line

in the output, along with any relevant element content. Algorithm 1 shows this process. A stack

is used to maintain the nesting of each element being output. The level where an element resides

dictates how its tag(s) and content are indented. The SVG document element has no indent. Its

children are output with a one-tab indent, its children’s children are output with a two-tab indent,

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 33

and so on. For purposes of this algorithm, an element comprising of only one tag (e.g.,

width="l" height="l"/>) is considered an end tag.

Algorithm 1 alg_LeadTab: Embedding in Leading White Space Require: SVG Document S V G Doc indent <= 0 for all tags in S V G Doc do if tag is a start tag then if parent start tag of tag exists and has not been written then stegOutput(parent start tag text, indent — 1) e n d if Push tag on stack indent <= indent + 1 else if tag is matching end tag to Peek of stack then Pop matching start tag off stack indent <= indent — 1 e n d if stegOutput(element text and tag text, indent) en d if e n d for

In stegOutputO (Algorithm 2), information is embedded in the indent such that a tab is

placed for a 0 bit and a tab-equivalent number of spaces is placed for a 1 bit.

Algorithm 2 Embedding Bits in Leading White Space Require: text to output text, indent > 0 leadingSpace <= for i = 1 to indent do if embedding 0 bit then leadingSpace <= leadingSpace concatenate tab character else leadingSpace •£= leadingSpace concatenate “ ” {Tab-equivalent number of spaces} en d if en d for O utput <= leadingSpace concatenate text

The method of extraction for alg_LeadTab, shown in Algorithm 3, is a simple reading of

each line in the stego file, reversing the embedding process.

The second and final algorithm that creates an SVG stego object by inserting leading

white space, alg_LeadTabSqOff, provides a slight variation to improve bandwidth over the previous

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 34

Algorithm 3 Extracting Bits from Leading White Space Require: SVG Stego Document SVG StegD oc NUM.SPACES JN.TAB 4 TAB JN.SPACES •$= “ ” {Tab-equivalent number of spaces} for all text Lines in SV G StegD oc do i -4= 0

w hile i < \ength(t ext Line) and (textLine[i] — il ” or textLine[i] — tab character) d o if textLine[i] = tab character th e n extract 0 bit i ■$= i + 1 else if textLine[i to i+ NUM_SPACESJN.TAB] =TABJN.SPACES then extract 1 bit en d if i

method. It does so by adding a limit to the length of a line output by the LeadTab algorithm,

potentially adding more lines for embedding information, and effectively wrapping, or “squaring

off” the text of the SVG source.

In place of the call to stegOutputO in Algorithm 1, logic is inserted to break down each

tag and its potential corresponding element text into lines for output that meet the line width

limitation. In our tests, lines of text in the SVG source were limited to 80 characters, including

indentations. There is a significant amount of code written (see Appendix Section B.1.1) to ensure

that line breaks occur so that the SVG remains well-formed and can be parsed by an SVG viewer.

Once valid output lines are formed, they are sent to stegOutputO (Algorithm 2) for final output.

For decoding, Algorithm 3 is used.

3.2.2 Adjusting Least Significant Figures

The first algorithm that creates an SVG stego object by adjusting least significant figures,

alg.TweakNums, replaces the least significant digit of numbers containing at least four significant

digits in the SVG cover to embed information. It attains the numbers that can be tweaked from

the list of allowed attributes in Table 3.1.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 35

Table 3.1. Attributes Allowing Adjustment

Attributes Description x, y, xl, yl, x2, y2, cx, cy, dx, dy, fx, fy, rx, ry, r Positioning attributes width, height Sizing attributes points polygon and polyline data d path and glyph element data transform Transform data startOffset Where text starts on a path to, values, keyTimes, keyPoints, dur Animation settings

The algorithm will perform the following steps to create an SVG stego object. First, as

each element is read in from the SVG cover file, it will check for any of the attributes listed in

Table 3.1. Second, for each of the allowed attributes that are found in the element, the algorithm

will parse out all of the numbers from its value and scan each one individually to see if it meets the

criteria for embedding. In this case, the criteria is that the number contains at least four significant

digits. Third, once qualified, the least significant digit will be adjusted to embed information, which

is described in the next paragraph. Fourth and finally, once information is embedded, the adjusted

attribute values are then re-combined with their associated element and output to the resulting

SVG stego object.

The alg.TweakNums algorithm embeds information by replacing the least significant digit

with an octal digit, effectively storing three bits per number adjusted. The embedding process is

shown in Algorithm 4.

It follows that the hidden information is extracted by going through the same steps that

were performed to create the stego object, with the exception that once a number is qualified, it

instead goes through the decoding process shown in Algorithm 5. This is essentially a conversion

of the least significant digit from octal to binary.

The second algorithm that creates an SVG stego object by adjusting least significant figures,

alg.TweakTwoNums, adds additional criteria so that it can tweak more digits. If a number contains

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 36

A lg o rith m 4 alg_TweakNums: Embedding an Octal Digit Require: number to tweak num newDigit <= 0 if first bit to embed is 1 then newDigit <= newDigit + 4 en d if if second bit to embed is 1 then newDigit <= newDigit + 2 en d if if third bit to embed is 1 then newDigit <= newDigit + 1 en d if Replace least significant digit of num with new D igit

A lg o rith m 5 alg_TweakNums: Extracting Information from the Least Significant Digit Require: least significant digit digit if digit > 4 th e n extract 1 bit digit 4= digit — 4 else extract 0 bit en d if if digit > 2 th e n extract 1 bit digit <= digit — 2 else extract 0 bit en d if if digit > 1 th e n extract 1 bit digit <= digit — 1 else extract 0 bit en d if

at least four significant digits and at least two of those digits are beyond the decimal point, then

that number’s two least significant digits are tweaked. If the number does not meet the additional

criteria but still has at least four significant digits, then only its least significant digit is tweaked as

in alg.TweakNums. Embedding and decoding are similar to Algorithms 4 and 5, with the exception

that when two digits are embedded or extracted, the process is repeated for the second least

significant digit.

The third and final algorithm that creates an SVG stego object by adjusting least significant

figures, alg.TweakTwoNumsMinErr, makes a relative adjustment to a number’s two least significant

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 37

digits to embed information and reduce the error incurred. The criteria for embedding—the number

of significant digits and digits beyond the decimal point required—are made variable to better

experiment with this technique. Also with each run of the algorithm, the number of bits to embed

per qualified number can be set, allowing anywhere from 1 bit per number up to 6 bits per number

(equivalent to alg.TweakTwoNums).

The embedding process of alg.TweakTwoNumsMinErr treats the two least significant digits

of a number in the SVG cover as a whole number, o E (0,99). To figure out how to adjust this

value to embed information, the set (0,99) is divided into intervals of 2b, where 6 is the number

of bits that can be embedded per number. We define the information to be hidden as a whole

number, x E (0,2h — 1). This information is embedded by replacing the original, o, with a new

number n in the interval closest to it, given n mod 2b = x. Suppose that we have the algorithm set

to embed 6 = 2 bits per number and that we would like to embed a value of x = 2. If the two least

significant digits of the number to tweak are 3 and 6, making o = 36, then we would substitute 3

and 8, making n = 38. Hence, when we go to extract we get our hidden information back: 38 mod

22 = 2. In the best case, if o = 38 (or 42, 46, 50, etc.), we wouldn’t have to adjust the original

number at all. This process is repeated on each number in the SVG cover object that meets the

criteria for embedding, similar to the other tweaking algorithms.

In our implementation of this algorithm the defined replacement of values is created as a

reference array in pre-processing, such that given o and x, array EmbedTable [o] [x] will provide n.

The process to create this array is specified in Algorithm 6.

3.2.3 Reordering Information

The first algorithm that creates an SVG stego object by reordering information,

algJteOrderElements, reorders SVG elements to hide information, where the set of SVG elements

at each level within the document are abstracted as lists. This requires each list to be able to have

a “natural” ordering put to it, so that a baseline can be established from which any variation hides

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 38

Algorithm 6 alg.TweakTwoNumsMinErr: Creation of Substitution Array for Embedding Require: number of bits to embed per number b intervalSize <= 2b EmbedTable 63 and j > 35 then EmbedTable[i] [j] <= j else if i > 95 and j > 3 then EmbedTable [*][)] <= (intervalSize x (95/intervalSize)) + j else EmbedTable[i][j] <*= ( intervalSize x (i/intervalSize)) + j end if end for end for

information. Given a naturally ordered list and the value to hide (given as a whole number), the

algorithm will perform calculations on the value to figure out where to place each item in the list.

The specifics are given in Algorithm 7. This approach is taken from that used by Gifshufile [32] to

hide information in the order of a GIF color table. Remember that there is a limit to the amount

of information that can be hidden, and a general rule of thumb is to look at the number of bits in

the value being hidden. Any combination of log2 (n\) — 1 bits can be hidden in a list with n items.

Algorithm 7 Hiding Information in the Order of a List Require: naturally ordered list OrderedList, value to hide val i 4= 2 j <= 1 StegList[ 0] -t= OrderedList[ 0] w hile j < \ength(OrderedList) do newPosition <= val mod i Insert OrderedList [)] at StegList[newPosition] {moving existing item(s) up in the list} val <= (val — new Position) / i i <= i + 1 j <=3 + 1 en d w hile

In order to retrieve hidden information from a list of items, the list is compared to a

naturally ordered version of the list to extract the value. The specifics are given in Algorithm 8.

The key is the variable numPrevInBoth, which for each item in turn it calculates the number of

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 39

items that fall before it in both the ordered and stego object lists. This value is rolled into val to

generate the hidden information in the form of a number.

A lgorithm 8 Retrieving Information from the Order of a List Require: stego object StegL ist OrderedList <= sortNaturally(5tegLi.s<) val <= 0 i <= length (OrderedList) —1 w hile i > 0 do j <= 0 numPrevInBoth <= 0 w hile StegList\j] ^ OrderedList[i] do if StegList[j] ^ null then numPrevInBoth <= numPrevInBoth + 1 e n d if j<= 3 + 1 en d w hile S teg L ist\j ] <= null val <= (val + numPrevInBoth) * i i <= i — 1 e n d w hile

Basically all internal entities under the document type declaration element and every

element under the document element, with the exception of any tspan elements, are available

for reordering. Text spans are not reordered because the embedding process could change the

arrangement of text to nonsense. The elements used for embedding are “naturally” ordered based

upon the Java String hash value of each element’s string representation. The hash value of a string

is computed as:

s[0] * 31"_1 + s[l] * 31"~2 + ... + s[n - 1] (3.1)

where s[i] is the ith character of the string and n is the length of the string.

Since it is possible for multiple elements to have the exact same text, the implementation

adds space to the end of one of the elements—after the last attribute but before the ending brace—

until the hash values of the two axe different. This does not affect rendering, but may stand out in

the source.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 40

The second and final algorithm that creates an SVG stego object by reordering information,

algJteOrderAtts, reorders SVG attributes to hide information. Given that attribute names cannot

be repeated within an SVG element, each attribute is “naturally” ordered based upon the hash

value of its name. Therefore, this implementation can be used together with another algorithm

th at adjusts attribute values, like alg.TweakTwoNums. This is because its hash value is independent

of the attribute value, whereas the hash value for each element in algJleOrderElements would

change if any text of the element were to change.

3.3 Test Results

In this section the usefulness of each method implemented in Section 3.2, along with some

currently available steganography tools, is measured through bandwidth and detectability. Table

3.2 contains a brief review of the algorithms and tools that were tested. The alg_AHMethods

algorithm was created to bring together all three general approaches implemented (reordering,

tweaking, and white space insertion). The SVG files used for testing appear in Appendix A. In

order to conduct similar tests with the other steganography tools, the SVG images were converted

into 24-bit Bitmap images, Graphics Interchange Format (GIF) images, and Joint Photographic

Experts Group (JPEG) images. In the case of Snow, the SVG source was used as a cover medium.

Though it was not formally assessed, the response time of all the algorithms implemented

could be measured in seconds on a laptop with an 800 MHz processor and 256 Mb of RAM. On the

same machine, Steghide and S-Tools took anywhere from minutes to hours to perform embedding.

This appeared to be due to the required memory utilization of these tools. Snow, like our algorithms,

responded quickly.

Bandwidth testing, shown in Section 3.3.1, is simply focused on maximizing this measure.

Detectability is assessed through various methods. First, with SVG having two human-readable

interfaces—both the rendered image and the textual source—perception in the stego object of

any visual cues showing changes from the cover object is reviewed in Section 3.3.2. In the future

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 41

Table 3.2. Algorithms and Tools Tested

File Type(s) Algorithm / Tool Tested Description algJLeadTab SVG Inserts sequences of tabs and tab-equivalent spacing at the beginning of each SVG source line that look like normal indentations. alg_LeadTabSqOff SVG Performs the same method as alg-LeadTab, but also runs a pre­ processing step that makes each line of the SVG source 80 characters in length at most, providing more lines for embedding. alg.TweakNums SVG Takes the least significant digit of numbers (only looks at those with four or more significant digits) in the SVG source and replaces it with an octal digit composed of three bits of the data to embed. alg.TweakTwoNums SVG Performs the same method as alg.TweakNums, also substituting the second least significant digit if and only if the number is a decimal and both digits being tweaked are on the right side of the decimal point. alg_TweakTwoNumsMinErr SVG Makes a relative adjustment to a number’s two least significant digits to embed information and reduce the error incurred. alg_ReOrderElements SVG Creates a “natural” ordering of SVG elements and then reorders them to embed data. alg_ReOrderAtts SVG Creates a “natural” ordering of SVG attributes within each element and then reorders them to embed data. alg-AllMethods SVG A combination of alg_ReOrderAtts, alg.TweakTwoNums, and alg.LeadTabSqOff. Snow[33J Text Files A tool for concealing messages in text files by appending tabs and spaces on the end of lines. Steghide[25J BMP, JPEG A tool for hiding information in audio and image files. Hides data in image files by taking a graph-theoretic approach and swapping pixels to embed information while maintaining first- order statistics. S-Tools[9] B M P , G IF A tool for hiding information in audio and image files. Hides data in image files by spreading the bits in a random walk through the least significant bits of the colors in the image.

there will probably be a binary version of SVG that should include encryption so that SVG source

cannot be read. Although this may draw attention to a particular SVG file, the explanation could

be simply the desire for a smaller package with some copyright protection, so it cannot be assumed

that secretive communications are occurring. In the second method used to assess detectability

(Section 3.3.3), the compression of both the cover and stego objects is conducted using WinZip,

with any differences in the compressed file sizes noted. And third, a simple, yet sensitive statistical

analysis is performed in Section 3.3.4 to compare the cover and stego objects.

3.3.1 Bandwidth

Bandwidth is measured according to the definition provided in Section 2.1.1. Table 3.3

shows the initial results. The expansion ratio column provides the percentage that the cover file

has grown as a result of embedding.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 42

Table 3.3. Bandwidth in Initial Testing

Algorithm / Tool File Type Average Bandwidth Average Expansion Ratio alg_AllMethods SVG 3.4% 13.2% alg_LeadTab SVG 0.1% 1.4% alg_LeadTabSqOff SVG 0.5% 13.2% alg-ReOrderAtts SVG 0.1% 0.0% alg_ReOrderElements SVG 0.2% 0.0% alg.T weakN ums SVG 2.3% 0.0% alg.TweakTwoN ums SVG 2.8% 0.0% Snow SVG 0.3% 3.1% Steghide BMP 4.2% 0.0% JPEG 5.5% 1.7% Steghide Average 4.8% 0.8% S-Tools BMP 12.5% 0.0% GIF 295.2% 418.8% S-Tools Average 153.8% 209.4%

Our insertion and reordering techniques did not attain very good bandwidth. For insertion,

this is due to the number of lines available in an SVG file being proportionally small to its overall

size, paired with our only embedding one bit per tab indent. Our insertion algorithms performed

in a similar fashion to Snow. Reordering is an improvement in that the file size does not expand,

but it too is very limited in bandwidth. In most of the SVG files tested, elements are grouped into

manageable sections with g elements, making for numerous smaller lists to order. Unfortunately, a

group of smaller lists holds less information than one large list. For example, a list of 100 items can

pack more bandwidth than 10 lists of 10 items each, which we can consider 11 lists by also ordering

the 10 smaller lists:

log2( 100!) = 525 > 11 ■ fo£j2(10!) = 240 (3.2)

Steghide and S-Tools provided the best bandwidth overall, but it is interesting to note that

with the more popular image formats used in Internet content, they experienced growth in file size.

This growth is dramatic with S-Tools embedding information in GIF images. Its manipulations of

the color table to hold information are not by any means subtle.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 43

Of our algorithms, the combination of all three approaches in alg_AHMethods provided

the best bandwidth, with most of it coming from the adjustment of least significant figures in

the SVG source. The alg.TweakTwoNums algorithm’s bandwidth of 2.8% represents an average of

about 8 kilobytes per cover file available for hiding information. Looking at the adjustment of least

significant figures alone, further analysis was conducted to both insert more information and reduce

the error incurred. Table 3.4 shows the results of these tests. Here, the root mean squared (RMS)

error is calculated to compare the original and tweaked numbers in the SVG files manipulated.

Table 3.4. Bandwidth in Testing of alg.TweakTwoNumsMinErr

N um ber of Digits Beyond B its Significant Decimal E m bedded Average of Average A lgorithm Digits Required Point Required Per N um ber B andw idth of RM S alg.TweakN ums 4 0 3 2.3% 1.711E-03 alg-T weakTwoN ums 4 0-2 4.083 2.8% 5.611E-03 1 1.6% 1.82E-03 2 3.1% 4.09E-03 3 4.7% 8.23E-03 3 0 4 6.2% 1.67E-02 5 7.8% 3.39E-02 6 9.4% 6.72E-02 1 0.8% 3.170E-04 2 1.5% 7.111E-04 3 2.3% 1.466E-03 0 4 3.0% 2.920E-03 5 3.8% 5.903E-03 6 4.6% 1.177E-02 alg_T weakT woN umsMinErr 4 4 0.7% 3.813E-04 1 5 0.9% 7.806E-04 6 1.1% 1.558E-03 4 0.7% 3.834E-04 2 5 0.9% 7.972E-04 6 1.1% 1.558E-03 3 1.0% 1.2382E-04 4 1.3% 2.7151E-04 5 0 5 1.6% 5.3424E-04 6 2.0% 1.0456E-03 3 0.8% 7.85158E-06 4 1.1% 1.56547E-05 6 0 5 1.3% 3.21456E-05 6 1.6% 6.33064E-05

In the alg.TweakTwoNumsMinErr algorithm, the criteria for tweaking were manipulated to

find a good balance between bandwidth and error incurred. As expected, requiring more significant

digits reduces the error incurred but also reduces bandwidth. A sharp drop off can be seen between

requiring four and five significant digits, due to the availability of this level of accuracy in the

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 44

SVG files tested. A similar drop off occurs when requiring digits beyond the decimal point for

tweaking. In one item th at stands out, alg.TweakTwoNums has a better bandwidth to error ratio than

alg.TweakTwoNumsMinErr with equivalent settings, but this is because it is lax in its requirement

for two digits beyond the decimal point, embedding in the least significant figure when decimal

digits are not found. Overall, alg.TweakTwoNumsMinErr is able to attain bandwidth results closer

to Steghide. Figure 3.1 shows RMS plotted against bandwidth with points of interest labelled. The

label “MinErr_3_0-4” refers to the use of alg.TweakTwoNumsMinErr requiring 3 significant digits

and 0 beyond the decimal point for tweaking to embed 4 bits per number tweaked. This case

appears to be the best balance between bandwidth and error incurred, but an acceptable RMS is

not known until visual (and potential statistical) effects are taken into account.

10.0% MinErr_3_0_6^ 9.0%

8.0% A Min Err 3 0 5 7.0% i 6.0% Min Err 3 0 3 1 5 .0% ------4 .0 % j CDg MinErr 4 0 5 3.0% ■algTweakTwoNums 2 .0% algTweakNums 1.0%

0 .0% 0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 RMS

Figure 3.1. Plot of RMS vs. Bandwidth

3.3.2 Visual and Structural Effects

Any visual effects incurred by each embedding method were searched for by placing each

cover object and stego object side by side and performing a visual scan. This enables the immediate

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 45

recognition of any major differences. In practice, one would hope that an attacker would not possess

a cover object for similar comparison. Though our other methods of testing aid in finding structural

effects, some axe noted here due to a basic understanding of the algorithm used in embedding.

For the most part, the insertion algorithms incurred no visual effects. In a couple of cases,

the text wrapping of alg_LeadTabSqOff caused text to be displayed with extra spacing due to

it flowing across multiple lines in the source. Structurally, the SVG source looks normal, but

in navigating the indentation white space, differing cursor movement over spaces and tabs calls

attention, and stands out to anyone editing the source. These embedding techniques are also found

if the source files are viewed in a text editor with a different tab indent length.

In the reordering of SVG elements undertaken in algJleOrderElements, the SVG painters

model comes into play. In most cases, the resulting SVG stego image is significantly different due

to the layering of graphical objects in the related cover image. Structurally, comment elements

are usually sorted away from the element(s) they reference. And in some cases, element identifiers

axe specified numerically in the cover object. Seeing them out of order in the stego object calls

attention. In alg-ReOrderAtts there are no apparent visual effects because attribute order is not

related to rendering. Structurally, looking over a set of similar elements grouped together in the

source calls attention to attributes listed in varying order.

The adjustment of least significant figures in numerical data by alg_TweakNums causes

little or no visual effects for most of the files tested. With a couple of them, at the highest

level of zoom provided by the Adobe SVG Viewer, differences can be found. One particular file,

world_population.svg, has many short lines that are drawn closely together in its rendering

of the European area of the map. Changes to these coordinates by all of our tweaking

algorithms created differences in these areas (see Figure 3.2). Also, in both alg.TweakTwoNums

and alg.TweakTwoNumsMinErr, the placement of graphical and text objects in relation to each

other—especially with text—stood out when comparing the cover and stego images. In looking for

an acceptable RMS, it appears to have to be less than 0.01 when tweaking these attribute values.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 46

Cover Image Stego Image

Figure 3.2. Visual Effects of Tweaking

On the better side, the SVG animations that were tested could handle the most tweaking,

with the animations that drew the entire image being the best. Given the nature of these animations,

it was difficult to compare them for any visual effects. Also positive about the tweaking algorithms

is their ability to not be recognized by a casual scan of the SVG source, an improvement over the

other embedding methods.

In the case of alg_AHMethods, it has the combined weaknesses of all the algorithms it

uses: alg_LeadTabSqOff, algJleOrderAtts, and alg.TweakTwoNums. This algorithm was a relevant

experiment to show how the different approaches could be used in concert, but putting the methods

together only improved bandwidth, and little at that.

Looking at the other steganography tools reviewed, the embedding of Steghide and S-Tools

showed no apparent visual effects. Snow, on the other hand, broke the SVG rendering on most of

the files due to the tool wrapping all text at 512 characters without regard to context. In viewing

the SVG source, the use of Snow could also be found through a simple highlight of the text. The

only other obvious structural effect incurred by any of these tools is seen when S-Tools is used

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 47

on GIF files. The color table is made to have many similar colors that only differ in their least

significant bits.

3.3.3 Compression Testing

Lossless file compression conducted by tools like GZip and WinZip rely on patterns within

files to be able to describe them using less information. Embedding extra information into a file

can potentially manipulate these patterns and effect the level of compression that can be attained.

Table 3.5 shows the difference in compressing the cover objects and each algorithm’s corresponding

stego objects using WinZip. Included for reference, the expansion ratio provides the percentage

that the cover file has grown as a result of embedding. For alg.TweakTwoNumsMinErr we show the

version of the algorithm with the highest bandwidth.

Table 3.5. Compression Testing Results

Average Average Algorithm / Tool File Type Expansion Ratio Compression Difference alg-AllMethods SVG 13.2% 1.0% alg.LeadTab SVG 1.4% 0.0% alg_LeadTabSqOff SVG 13.2% -0.4% algJleOrderAtts SVG 0.0% 0.4% algJleOrderElements SVG 0.0% 0.9% alg.TweakN ums SVG 0.0% 1.0% alg.TweakTwoN ums SVG 0.0% 1.3% alg_T weakT woN umsMinErr_3_0_6 SVG 0.0% 2.4% Snow SVG 3.1% 0.1% Steghide BMP 0.0% 13.7% JPEG 1.7% 1.9% Steghide Average 0.8% 7.8% S-Tools BMP 0.0% 20.9% GIF 418.8% -0.9% S-Tools Average 209.4% 10.0%

Changes due to the insertion of white space and the reordering of information have little

effect, with even the tweaking algorithms not dramatically changing this statistic. The most

interesting result comes from Steghide and S-Tools, where both see noticeable increases when

embedding information into 24-bit Bitmap images. It is especially interesting in Steghide, which

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 48

maintains the first-order statistics of the image by swapping pixels to embed information. Further

study would have to confirm that these tools have this effect on a broader set of test images.

3.3.4 Statistical Analysis

The final test conducted is a basic statistical test of confidence in the difference between

the cover objects and each algorithm’s corresponding stego objects. This is performed using a chi-

square test, which is a very sensitive statistical test of differences between two distributions. For

our distributions, we count the number of occurrences of characters in each SVG file. Given that

we know exactly what each algorithm is changing, we only count those characters. For the insertion

algorithms—to include the tool Snow—this includes tab, space, and end-of-line characters. For the

tweaking algorithms, only the ten numeric characters are counted. The reordering algorithms are

not included in this testing as they do not make a significant number of character substitutions.

With Steghide and S-Tools, we count the number of occurrences of each byte in the file.

We define an “item” as a character or byte that is being counted. Let n equal the number

of different items being counted. Let o; be the original number of occurrences of item i in the cover

file. If Oi < 10 for any item being counted, it will be excluded from this calculation. Let si be the

length of the stego file (in bytes) and cl be the length of the cover file (also in bytes). Then our

expected value for each item i is defined as:

(3.3)

Using this, the difference in file size between the cover and stego files is taken into account.

Let the actual value, a*, be the number of occurrences of item i in the stego file. Given this, we

calculate:

(3.4)

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 49

The chi-squaxe distribution is then evaluated at X with n — 1 degrees of freedom to provide

a confidence value, C, pertaining to the similarity of the two distributions. We take 1 — C to

get the confidence that they axe different, shown in Table 3.6 for each algorithm tested. The

label “alg_TweakTwoNumsMinErr_3_0_4” refers to the use of alg.TweakTwoNumsMinErr requiring

3 significant digits and 0 beyond the decimal point for tweaking to embed 4 bits per number tweaked.

Given the sensitivity of this measure, values over 95% are considered significant.

Table 3.6. Confidence Values that Cover and Stego are Different

A lgorithm Type Average Confidence alg_AllMethods S V G 100.0% alg_LeadTab S V G 10 0 .0 % alg_LeadTabSqOff S V G 10 0 .0 % alg.T weakN ums S V G 9 7 .8 % alg.T weakT woN ums SVG 9 7 .8 % alg_TweakTwoNumsMinErr_3_0-l SVG 59.6% alg.TweakTwoNumsMinErr.3.0.2 SVG 71.8% alg_TweakTwoNumsMinErr_3_0-3 SVG 84.6% alg.TweakTwoNumsM inErr .3 .0.4 S V G 9 8 .6 % alg.TweakTwoNumsMinErr .3 .0.5 SVG 100.0% alg.TweakTwoNumsM inErr .3 .0.6 S V G 100.0% alg_TweakTwoNumsMinErr_4_0_l SVG 57.7% alg_TweakTwoNumsMinErr_4_0_2 SVG 51.8% alg.TweakTwoN umsMinErr.4.0.3 SVG 66.7% alg.TweakTwoN umsMinErr.4.0.4 SVG 67.4% alg.TweakTwoNumsMinErr.4.0.5 SVG 71.0% alg.TweakTwoNumsMinErr_4_0_6 SVG 94.1% alg.TweakTwoNumsM inErr .4.1.4 S V G 1 0 0 .0 % alg.TweakTwoN umsM inErr_4_l .5 S V G 1 0 0 .0 % alg.TweakTwoNumsM inErr .4.1.6 S V G 1 0 0 .0 % alg.TweakTwoNum sM inErr .4 _2_4 S V G 1 0 0 .0 % alg.TweakTwoNumsM inErr .4 .2.5 S V G 1 0 0 .0 % alg.TweakTwoN umsMinErr_4_2_6 S V G 1 0 0 .0 % alg.TweakTwoNumsMinErr-5-0.3 SVG 50.0% alg.TweakTwoNumsMinErr.5.0.4 SVG 50.4% alg.TweakTwoNumsMinErr.5.0.5 SVG 52.4% alg.TweakTwoN umsMinErr.5.0.6 SVG 53.0% alg.T weakTwoN umsMinErr.6.0.3 SVG 50.4% alg.TweakTwoNumsMinErr.6.0_4 SVG 50.9% alg_TweakTwoNumsMinErr.6_0.5 SVG 51.6% alg.TweakTwoNumsMinErr .6.0.6 SVG 56.7% S n o w S V G 1 0 0 .0 % BM P 66.7% S te g h id e J P E G 1 0 0 .0 % BMP 100.0% S -T o o ls G IF 1 0 0 .0 %

All of the insertion techniques, including the tool Snow, fail this test since they so

dramatically change the number of white space characters. Similarly, S-Tools, with either format

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 50

it uses, and Steghide using JPEG images do not pass. On the other hand, Steghide using 24-bit

Bitmap images is able to preserve this statistical property of the cover file.

In the adjustment of least significant figures in numerical data by alg.TweakNums and

alg.TweakTwoNums, octal digits are inserted. Therefore, it’s no surprise that both of these

algorithms fail this test, since the stego files they output would have significantly fewer occurrences

of 8’s and 9’s than exist in the cover file.

This leaves the alg.TweakTwoNumsMinErr algorithm, which has mixed results since we axe

varying both the embedding criteria and the amount of information being embedded. Failure of

this test appears to happen when more numbers in the cover file are being tweaked and a higher

amount of information is being embedded into each of them. This makes sense, as with higher

embedding rates, the interval that the algorithm uses to select from is larger, providing little or no

repetition when random data is embedded. Therefore, the portion of the cover file that is changed

by the algorithm would likely not match the distribution of data that exists elsewhere in the cover

file. Further exploration is required to assess the statistical properties of SVG files.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. CHAPTER 4

CONCLUSIONS AND FUTURE WORK

In our exploration of Scalable Vector Graphics (SVG) files as a cover medium for

steganography, numerous opportunities for future research have come to light. The embedding

methods that have been developed in this work are a first look at the problem of balancing

bandwidth and detectability when using SVG as a cover. One of the more interesting aspects

of SVG is that it provides the capability to zoom without loss of image quality. This effects any

steganographic algorithms operating in the spatial domain. Also, given that SVG source is human-

readable, this enables not only the re-use of content, but also the increased potential to discover

the use of steganography. The insertion of white space to embed information was proven ineffective

given its ease of detection in this manner paired with its bandwidth limitations.

More robust techniques used for digital watermarking operate in some sort of transform

domain [29] and do not directly substitute cover information, but rather manipulate it within some

bounds. The reordering of SVG source content does not provide enough bandwidth to justify

research into variant methods that work within the SVG painter’s model. But future research

should look to other transformation techniques that can be applied to implement steganography

with SVG.

The most promising algorithm assessed in this work was the adjustment of the least

significant figures in numerical SVG data, or “tweaking” as it was termed. Further research should

51

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 52

be conducted into which SVG attributes should and should not be tweaked—and to what extent—

defining thresholds for how many significant figures could be adjusted within each attribute’s

data. This would require a more in-depth study of visual effects and possibly the development

of better detectability testing, which could better define some common statistical properties of

SVG images. Similar work has been conducted in [41], where thresholds for embedding are defined

for the technique that was used. This attests to the tenet that states as the amount of information

attempting to be hidden increases, the likelihood of detection also increases.

There is also room for improvement in the implementation of the alg.TweakTwoNumsMinErr

algorithm. For instance, the current algorithm in most cases only looks to values equal to or greater

than the original number for the closest matching substitution. It could instead be written to look

for the closest value amongst those greater than, equal to, or less than the original number to

further reduce error.

Future work should include a study of the computational complexity of the algorithms

addressed in this work. Other embedding algorithms created in the future can be easily integrated

into the current source code for assessment. Also, a study addressing steganalysis using both active

and malicious attacks against methods using SVG as a cover should be undertaken.

Scalable Vector Graphics files can serve as a cover medium for steganography, though there

are distinct challenges in using the format to hide information. These include, but are not limited

to, its readable source content and the ability to cleanly enlarge images to any desired level of

zoom. If the use of SVG becomes more common, then this research can be the starting point for

the development of better methods of embedding information and the creation of useful tools to

implement steganography. This is a distinct possibility if SVG Mobile catches on as a vector image

content medium for mobile devices, which these days outnumber personal desktop computers.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. APPENDIX A

SVG FILES USED IN TESTING

The Scalable Vector Graphics files used in testing are listed in Table A.I. Given its

usefulness in creating maps with various underlying data sets to display, most of the SVG files

used in our testing are maps. Figures containing each SVG image follow the table.

Table A.I. SVG Files Used in Testing

Source Figure # Test File Description h t t p : //m m .carto.net/papers/svg/samples/animated_bustrack.shtml A.l animated.bustrack Map showing a bus route through a town using SVG path animation to trace th e route. h t t p : //m m .carto.net/papers/svg/samples/path_animation.shtml A.2 animated-plane Map showing the path of a plane, using SVG animation to move the object over a map and highlighting the cities visited/passed. JavaScript (separate file) is used to allow the user to adjust variables of the anim ation. h t t p : //mm. carto. net/papers/s vg/samples/ A.3 gaenseblume Picture of a flower that uses SVG animation to adjust the opacity of the SVG drawn portions of the flower as well as the raster background image so that the final picture comes gradually into view. SVG path animations are used to show portions of the SVG drawn flower as the opacity is adjusted in stages. JavaScript inserts the animation elements on load. h t t p : //m m . carto .net/papers/svg/samples/ A.4 krokus Picture of a flower that uses SVG animation to adjust the opacity of the SVG drawn portions of the flower as well as the raster background image so that the final picture comes gradually into view. SVG path animations are used to show portions of the SVG drawn flower as the opacity is adjusted in stages. JavaScript inserts the animation elements on load. h t t p : / /m m . carto. net/papers/svg/samples/wien. shtml A .5 Vienna Map showing census data (multiple variables & years) of Vienna, Austria. User interface and data are in separate JavaScript files with all the presentation in SVG. h ttp : / /mm. dbxgeomatics. com/products/svgmapmaker/samples/vorld-populat ion. svg A.6 world.population Map showing population data of the World. Pie charts are sized by the total population of a country, and show it broken out by age range.

53

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 54

0 7 # Z u r ic h H o n g g e r b e r g

Figure A.I. animated.bustrack.svg After Animation

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 55

Bern

Start Animation SpMdUp j S p a a d O o w n [ C urrent Duration is: 10 aecam fs

Figure A.2. animated-plane. svg During Animation

Figure A.3. gaenseblume. svg During Animation

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. Figure A.4. krokus. svg During Animation

V ienna Social patterns and structures Map Navigator Dreg RcctangiatB ...... v"V ... • v divgeaxwntn .. s - r* 'ym '-j* V - ibu» map ; t.4. >;v.. J ' S ' f f Zecmfccwr 100% , >.

Salact Map Uyw UEpapumadOotraa Sattioa A ttm gj Q Gna»n<3*«a(taefder*ri SuUtaah»5 SoloctAVfcriaMato Map

At Va&e* r< Ptizert Select iMjmbar of CImim

CafcUtttCdMSkQutrCte* f^cwr i> !

w * « Lecpesdst** 902 S U M Otservr P rw r i3020106) rSub-OMrtet 25 « % : * * »

Poopto oMv than *0 (1*71) ■ ■ H D D . 37.w ■x.n «r.» .a n imo

Figure A.5. Vienna.svg

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 57

WORLD POPULATION

ATLANTIC O C f-AN

PACtWG OCEAN

Figure A.6. world_population.svg

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. APPENDIX B

SOURCE CODE

The source code written to use SVG as a cover medium for steganography is described in

the following sections. Section B.2 contains all of the source code listings.

B.l Overview

The source code to implement various steganography methods using Scalable Vector

Graphics (SVG) as a cover medium was written in the Java™ programming language (J2SDK

1.4.2). It is composed of twenty-five classes and one interface, taking advantage of inheritance

and method overriding to exploit common code. The general challenges and common code of

the project will be discussed in this section with a detailed description of the implementation of

each embedding method to follow. This implementation is for the purpose of testing methods of

embedding information into SVG files and therefore is not a fully functional tool. On the other

hand, it was written so that other methods of embedding could be implemented and plugged into

its framework with little effort.

The driver for the program is the SVGSteg class (Listing B.l). Its m ain() method takes

four arguments to either encode (i.e., hide) or decode (i.e., extract) information. The first is an ’e’

for encode or a ’d’ for decode, followed immediately by a one-character abbreviation for the method

to be used. The third argument depends upon whether an encode or decode is taking place. On an

58

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 59

encode, it is the SVG cover file to use to hide information; for decode it is the SVG stego object

where hidden information is to be extracted. The fourth and final argument specifies the seed for

the random number generator used to compose a sample set of information to hide. This argument

is optional, with it defaulting to zero. Simply running java SVGSteg will provide usage information,

to include all possible methods of embedding information that can be used.

Any class implementing a method of embedding must implement the StegMethod interface

(Listing B.2) and have an ABBREV constant defined for its one-character abbreviation. This way

the SVGSteg class knows how and when to use it. One other general requirement adhered to for

this implementation was that each method implemented writes its stego object output to a new

file, so that for testing purposes the original cover work and the stego object can be compared. In

general you may want to overwrite the cover work so that it could not be acknowledged that any

previous work existed. Multiple copies of the same file could certainly raise suspicion.

The StegAlgorithm class (Listing B.3) provides a method of testing correctness for all of

the algorithms implemented in this work. It defines an instance of the Inf oGen class (Listing B.4),

which is responsible for generating random bits for embedding. To test if an algorithm has correctly

embedded the information, on decode this instance is invoked again to generate the same pseudo­

random bit stream (provided the same seed is given on decode that was provided for encoding).

The StegAlgorithm class uses this to check each bit to ensure that what was embedded and what

is being extracted are the same. This class implements the StegMethod interface, so that all the

embedding methods can implement it and inherit these checks.

In order to parse SVG files, at first the Batik SVG Toolkit [4] and Xerces2 XML parser

[27] were considered. The major problem encountered with using these tools was in the handling of

internal entity references. Looking at the animated_bustrack. svg test file source, it contains 46

internal entity references, all of which resolve style attribute values within the document. This is

common in SVG files produced from a graphical authoring tool like . During the

parsing of the file, all of the internal entities were automatically resolved, significantly increasing

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 60

the size of the file output beyond that input by more than 63%. Such a change in file size throws

up many red flags and practically gives away that a file contains more than meets the eye. Due to

this and the deeper level of control desired for embedding information, the TextManip class (Listing

B.5) was written.

The TextManip class provides very basic functionality for the parsing and output of SVG

files, replacing the need for a standard XML parser. In line with this, the SVGTag and SVGAttribute

classes (Listings B.6 and B.7) implement an object model for SVG tags and attributes respectively.

The TagProcessor (Listing B.8) is the last general class, providing a framework for the embedding

methods that process SVG tags to hide information. It uses the parsing and output of the LeadTab

algorithm (detailed in Section B.1.1), but only puts leading tabs on each line to format the output

nicely.

B.1.1 Inserting White Space

The first method of embedding information in Scalable Vector Graphics files implemented

is that of inserting white space in the source. The LeadTab class (Listing B.9) embeds information

using leading tabs and spaces on each line of an SVG source file, as described in Section 3.1.1.

It contains two constants: NUM_SPACES_TAB and TAB_IN_SPACES, which define a tab-

equivalent number of spaces and a string containing a tab-equivalent number of spaces respectively.

They have been set such that four spaces is equivalent to one tab jump.

As a variation on this method, the LeadTabSqOff class (Listing B.10) adds a limit to the

length of a line output by the LeadTab algorithm, in the hopes of providing more output lines for

embedding. The line limit is defined by the constant NUM _CHARS, which is set to 80 characters.

B .l.2 Adjusting Least Significant Figures

The second method of embedding information implemented is the exploration of adjusting

the least significant figures of many of the numbers in an SVG source file. The TweakNums class

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 61

(Listing B .ll) accomplishes this per the algorithm described in Section 3.2.2. In TweakNums,

a NUM_DIGITS constant is defined that requires a number to have at least four significant

digits to qualify for tweaking. Upon meeting that criteria, its least significant digit is tweaked.

The TweakNums.NumEncoder and TweakNums.NumDecoder classes carry out the core of the

implementation.

As a variation on this implementation, the TweakTwoNums class (Listing B.12) adds

the tweaking of another digit given a number that matches its extended criteria. Using the

NUM .DIGITS constant defined in TweakNums, if a number has at least four significant digits

and, in addition, at least two of those digits are beyond the decimal point, then its two least

significant digits are tweaked. If a number has at least four significant digits but does not meet

the additional criterion, then just its least significant digit is tweaked. Inheriting common methods

from those in TweakNums, the TweakTwoNums.NumEncoder and TweakTwoNums .NumDecoder classes

carry out the core of the implementation.

In a third variation, the aim is to reduce the error introduced by tweaking. The the

TweakTwoNumsMinErr class (Listing B.13) defines three constants: N U M _S IG _D IG IT S and

NUM_DIGITS_BEYOND_DEC to set criteria for embedding and NUM-BITS to set the

amount of information to embed in each number that meets the criteria. Though it takes a different

approach to embedding than the other two implementations in this section, this implementation

inherits from TweakNums and works within its framework.

B.l.3 Reordering Information

The third method of embedding information implemented is the reordering of information

in the SVG source file. The OrderEncoder class (Listing B.14) implements the general hiding

of information in the order of items in a list. The ReOrderElements class (Listing B.15) uses

the OrderEncoder to implement the reordering of SVG elements to hide information. To provide a

“natural” ordering of elements, the OrderedSVGDoc class (Listing B.16) was written to represent the

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 62

entire document, which contains instances of the OrderedSVGTag class (Listing B.17) for each tag.

The SVG document element is represented by the SVGDocElem class (Listing B.18), a specialization

of an OrderedSVGTag. The ability to adjust the string representation of elements to make them

distinct for ordering is only available because of the deeper level of control that we have over output

with the TextManip class described in Section B.l.

As a variation, the ReOrderAtts class (Listing B.19) uses the OrderEncoder to implement

the reordering of SVG attributes within each element to hide information.

B.l.4 Combining Methods

In the quest for more bandwidth the AllMethods class (Listing B.20) was written to

combine three of the implementations. It takes an SVG cover file and applies ReOrderAtts, then

TweakTwoNums, and finally the LeadTabSqOf f implementation to arrive at a stego object. The only

requirement in putting these implementations together was that the reordering had to take place

before the tweaking of numbers, since the flow of hidden information in the tweaking algorithm

relies on the order in which adjusted numbers axe processed.

Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 63

B.2 Source Code Listings

Listing B.l. SVGSteg. java im port java.io.File;

/** * The SVGSteg class for embedding information into SVG files. *

Completed 8/28/04 *

Update History: * < u l> *

  • 3/3/06: Added TweakTwoNumsMinErr to methods.
  • *
  • 2/21/06: Added NumbersTweaked to methods.
  • * < /u l> * @author Jerry Hungerman */ public class SVGSteg { /** * This method is the entry point into the SVGSteg program. * ©param args Program arguments (use no arguments to see usage) */ public static void main(String[] args) { String strFile = null; //SVG cover file name boolean booEncode = false; / /True if information is to be hidden char charMethod = ’\0’; //Steg method long IngSeed = 0; /^Random number seed for generating random d a ta * / File fileSVG; //The SVG cover file StegMethod objMethod = null;//Class for embedding/extracting information

    / / Get the arguments from the command line for (int i = 0; i < args.length; i++) { if (i == 0){ if (a rg s [i ]. len g th () != 2){ showUsage(); } e ls e { //Encode or decode if ( args [ i ]. char At (0) = = ’e’){ booEncode = true; } else if (args [ i ]. char At (0) = = ’d’){ booEncode = false; } else showUsage();

    //G et the steg method charMethod = args[i].charAt(l); } } else if (i == 1){ //G et the SVG cover file name strFile = args[i ]; } else if (i == 2){ / /Random number seed for generating information to embed IngSeed = Long.parseLong(args[i]); } e lse { //Too many arguments showUsage(); } }

    //Verify a file was specified if (strFile == null) showUsageQ;

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 64

    / /Make sure the SVG document exists fileSVG = new File(strFile); if (fileSVG.exists() = = false){ System.out.println(”The~fiIeM’” + strFile + ” ’-doesJiot.jexist.” ); System.exit(l); }

    //T he NumbersTweaked method is only meant to check what is embedded / / and therefore is not to be used for encode if ((charMethod==NumbersTweaked.ABBREV) && (booEncode==true)){ showUsage(); }

    //G et the proper class according to the embedding method specified switch(charMethod) { case LeadTab.ABBREV: objMethod = new LeadTabQ ; break; case LeadTabSqOff.ABBREV: objMethod = new LeadTabSqOff(); break; . case TweakNums.ABBREV: objMethod = new TweakNums(); break: case TweakTwoNums.ABBREV: objMethod = new TweakTwoNumsQ; break; case NumbersTweaked.ABBREV: objMethod = new NumbersTweaked(); break; case TweakTwoNumsMinErr.ABBREV: objMethod = new TweakTwoNumsMinErr(); break; case ReOrderElements.ABBREV: objMethod = new ReOrderElements(); break; case ReOrderAtts.ABBREV: objMethod = new ReOrderAtts(); break; case AllMethods.ABBREV: objMethod = new AllMethods(); break; } if (objMethod == null){ showUsage(); }

    / /Encode or decode according to what the user entered if (booEncode == true){ System.out.println(” Successfully-embedded-” + objMethod.encode(fileSVG, IngSeed) + ’’-bytes”); } else { if (objMethod.decode(fileSVG, IngSeed) = = true){ System.out.println(” Decode-Successful”); } else { System.out.println(” Decode-Unsuccessful”); } } }

    /** * This method displays the proper usage of the program.

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 65

    */ private static void showUsage() { System.err. println (” Usage: J3VGSteg---[seed]”); System.err. println (); System.err. println (”e_=_Embed”); System.err. println (”d~=.Decode”); System.err. println (” Steg_Methods”); System.err. println (” ” + LeadTab.ABBREV + ”_=_Leading_tab/spaces”); System.err. println + LeadTabSqOff. ABBREV + ” -=_Leading-t ab / spaces-(squaring-text~oflLwith-a_line -limit) ”); System.err. println (” ___ ” + TweakNums. ABBREV + ” -=-Tweak_least_significant_digit” ); System.err. println (” ___ ” + TweakTwoNums.ABBREV + ” -=-Tweak_two_least-significant-digits”); System.err. println + NumbersTweaked. ABBREV + ” .^= -Look_at-numbers ~being_tweaked_by~TweakTwoN ums_(decode^only)”); System.err. println (” ” + TweakTwoNumsMinErr.ABBREV + ”-=-Tweak_two_least^ignificant„digits^embedding-l-—^6wbitsJ’ + ” (set„bywa_constant_in~the_code)~and_minimizing_variation -from-” + ’’th e^ o rig in al” ); System.err. printing”, ” + ReOrderElements. ABBREV + ” -= Jte o rd e r^ S VG _tags” ); System.err. println (”, ___ ” + ReOrderAtts. ABBREV + ” _= _Reorder_S VG_tag_attributes”); System.err. println (” ____ ” + AllMethods. ABBREV + ” „= ,-Run_a~combination_of~the-above-methods_(reorder_attributes, J ’ + ” basic_tweak..two,~&;.square.x>ff)”); System.err. println (” coverfile -=-The_cover_file_to-use”); System.err. println (”seed~=_Seed Jo r jandom jiumber^generationJ’ + ” (default - is j O)” ); System.exit(l); } }

    Listing B.2. StegMethod.java import java.io.File;

    /** * The StegMethod interface for any class implementing an SVG * steganographic method. *

    Completed 5/8/04 * @author Jerry Hungerman */ public interface StegMethod { public float encode(File p_fileSVG, long pJngSeed); public boolean decode(File p-fileSVG, long p_lngSeed); }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 66

    Listing B.3. StegAlgorithm. java im port java.io.File; /** * The StegAlgorithm class for implementing common functionality * to be used by SVGSteg algorithms. *

    Completed 5/8/04 * @author Jerry Hungerman */ public class StegAlgorithm im plem ents StegMethod { /** * Generates the information to embed */ protected InfoGen m-infoEmbed; / ** * For decoding: True so long as what is read matches the pseudo—random * stream */ protected boolean nubooMatches; /** * This method encodes information in an SVG file. Needs to be overridden * by subclass for desired functionality . * ©param pJileSVG SVG file to embed information into * @param pJngSeed Random number seed for sample data to embed * @return The number of bytes successfully encoded */ public float encode(File pJileSVG, long pJngSeed)} System.out.println(” Needuto^override_encode()_for_encode.”); return (float)O; } /** * This method decodes information from an SVG stego object. Needs to be * overridden by subclass for desired functionality . * @param pJileSVG SVG file to extract information from * ©param pJngSeed Random number seed for sample data to embed * ©return True if the embedded information matches what is * generated with the seed passed in */ public boolean decode(File pJileSVG, long pJngSeed) { System.out.println(” Need_to-override_decode()_for jiecode.”); return false; } /** * This method will check that the next binary digit matches what is * expected. * @param pjntB it Bit to check */ protected void processBit(int pjntBit) { / /The next bit should match that hidden in the stego object if (mJnfoEmbed.next() != pjntBit){m.booMatches = false;} } /** * This method will check that the next set of binary digits match what is * expected. * @param pJntNumBits Number of bits to check * ©param p-intData Number containing the data */ protected void processBits(int pJntNumBits, int pjntData){ if (mjnfoEmbed.nextBits(pJntNumBits) != pjntData){ m.booMatches = false; } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 67

    Listing B.4. InfoGen.java im port java.util.Random;

    * The InfoGen class for generating pseudo—random information for * embedding. *

    Completed 5/8/04 *

    Update History: * < u l> *

  • 3/3/06: Added method {@link //next.BiTs(in 1.) n e x tB its} .< /li> *
  • 12/l/05: Added method {@link #nextBytes(int) nextBytes}.
  • * < /u l> * @author Jerry Hungerman */ public class InfoGen { /** * Java’s pseudo—random number generator */ private Random m_ranGen;

    /** * Keeps track of the number of bits generated */ private long mJngBitsGen = 0;

    /** * The constructor for the class . * @param pJngSeed Seed for the pseudo—random number generator */ public InfoGen(long pJngSeed) { m_ranGen — new Random(pJngSeed); } /** * This method returns the next bit in the pseudo—random stream. * @return The next bit in the pseudo—random stream */ public int next(){ m_lngBitsGen++; if (m_ranGen.nextFloat() < .5){ r e t u r n 0; } e ls e { r e t u r n 1; } }

    /** * This method returns a byte array from the pseudo—random stream. This * could certainly be done more efficiently but is written in this manner so * that with the same seed the same bit stream will be generated as when the * method {©link #next next} is used. * C'iparam pJntSize Number of bytes to generate * ©return Byte array containing a pseudo—random stream */ public byte[] nextBytes(int pJntSize) { byte[] theData = new byte [pJntSize]; / /Byte array to output int intByte; //Integer representation of byte

    / /Populate the byte array to fill the file fo r ( in t i = 0; i < p Jn tS iz e; i+ + ){ / / Initialize

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 68

    intB yte = 0;

    //Bytes in Java can be represented as signed integers , so the first //bit is the sign bit if (next() == l){intByte = —128;}

    //Generate the rest of the byte and add it to the byte array for (int j = 64; j > 0; j = j/2){ intByte = intByte + (j * next()); } theData[i] = Byte.parseByte(Integer.toString(intByte)); } return theData; } /** * This method returns an integer from the pseudo—random stream, using only * the number of bits given (e.g., 3 bits provides an integer in (0,7)). * This could certainly be done more efficiently but is written in this * manner so that with the same seed the same bit stream will be generated * as when the method {©link #next next} is used. * @param pJntNumBits Number of bits to generate (Maximum of 31 as Java’s * integer is a 32—bit signed integer) * ©return integer containing pseudo—random bits, limited by pJntNumBits */ public int nextBits(int pJntNumBits)} int intTheNum; //Number generated int intStart; //Value of high order bit

    / /Make sure the parameter is valid if ((pJntNumBits < 1) || (pJntNumBits > 31)){ System.out.println(” Erron-ParametenjOUtuof-range Jn J ’ + ”InfoGen.nextBits()”);}

    // Initialize and get the maximum value for the high order bit intT heN um = 0; intStart = new Double(Math.pow(2, (pJntNumBits — l))).intValue();

    //Build the integer from the random stream fo r ( in t i = in tS ta rt; i > 0; i = i/2 ){ intTheNum = intTheNum + (i * nextQ); } return intTheNum;

    /** * This method returns the number of bytes produced by the pseudo—random * stream . * @return The number of bytes produced by the pseudo—random stream */ public float bytesProduced(){ return mJngBitsGen/(float)8; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 69

    Listing B.5. TextM anip. ja v a im port java.io.File; im port java.io.FileWriter; im port java.io.FileReader; im port java.io.BufferedWriter; im port java.io.BufferedReader; im port java.io.IOException;

    /** * The TextManip class for manipulating the source of an SVG file * to embed information. *

    Completed 8/28/04 *

    Update History: * < u l> *

  • 2/17/06: Modified {@link #readTag() readTag} method to preserve * whitespace in between SVG <text> elements and in between * SVG <g> elements. This change was made due to * JavaScripting in vienna.svg looking for a child node under these types of * elements to dynamically substitute data.
  • * < /u l> * @author Jerry Hungerman */ public class TextManip { /** * Buffered reader for reading text from the input SVG file */ private BufferedReader m_brln; /** * Buffered writer for writing text to the output SVG file */ private BufferedWriter m.bwOut;

    /** * Cache of what is being read in */ protected String m_strCache;

    /** * True if currently processing the CDATA section */ protected boolean m.booInCDATA;

    /** * The constructor for the class . * @param p_fileSVG SVG file to open and read from * @param p-strExt Information to tack on to file name to create output SVG * file . If null is passed in, only the reader is set up. */ public TextManip(File p-fileSVG, String p_strExt){ String strName; //Name of the output SVG file String strPath; //Path of the output SVG file File fileOut; //SVG file for output

    / / Initialize m.booInCDATA = false; if (p-strExt == null){ try { //O nly initialize the reader m_brln = new BufferedReader(new FileReader(pJileSVG)); } catch (Exception e){

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 70

    System.out.println( e); } } else{ //G et the output file name strName = p_fileSVG.getName(); strName = strName.substring(0, strName.length() — 4); strName = strName + p_strExt;

    //G et the output path strPath = p.fileSVG .get Parent (); if (strPath != null){ strName = strPath + Pile, separator Char + strName; } fileOut = new File(strName);

    try{ //Create the output SVG File, deleting a previous version if / / necessary if (fileOut .createNewFile()==false){ if(fileOut. delete()==false){ throw new Exception(”Error-creatingjOutput_SVG-file-” + + strName + } else{ if (fileOut. createNewFile()==false){ throw new Exception(” Error-xreating-output J3VG_fileJ’ + + strName + } } }

    // Initialize the reader and writer m_brln = new BufferedReader(new FileReader(p-fileSVG)); m_bwOut = new BufferedWriter(new FileWriter(fileOut)); } catch (Exception e){ System.out.println(e); } } } /** * This method closes the files the object has open. */ public void closeFilesQ { *ry{ if (m.brln != null) {m_brIn.close();} if (m.bwOut != null) {m_bwOut.close();} } catch (Exception e){ System.out.println(e); } } /** * This method closes the files the object has open before garbage * collection . */ protected void finalize(){ closeFiles (); } /**

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 71

    * This method reads the next tag from the input SVG file. * ©return The SVG tag if one exists, otherwise null */ protected SVGTag readTag(){ String strText = null; //Leading text of a tag String strFullTag = null; //Full text of the tag int i; //Counting variable int j ; //Counting variable

    try { //Fill the cache if needed if (m_strCache == null){ m_strCache = m_brIn.readLine(); if (m_strCache == null){ return null; } e ls e { m_strCache — m_strCache.trim(); } }

    //If we are in CDATA, get all of the data as leading text and //return the end tag while (m.booInCDATA == true){ if (strText == null){strText = ””;} i = findInC ache(’> ’); if (i == —l){return null;}

    if (m_strCache.substring(i — 2, i + l).equals(”]]>”)){ strText = strText.concat(m_strCache.substring(0, i — 2)); m.booInCDATA = false; if (m-strCache.length() = = i + 1){ m-strCache = null; } else{ m-strCache = m_strCache.substring(i + 1); }

    / /Return the CDATA end tag with leading text SVGTag objTag = new SVGTag(”]]>”, strText); return objTag; } e ls e { strText = strText.concat(m_strCache.substring(0, i + I)); m_strCache = m-strCache.substring(i + 1); } }

    / /Look for the opening of a tag i = findInCache(’< ’); if (i == —l){return null;}

    / / Get any leading text if (i != 0){ strText = m_strCache.substring(0.i);

    //M ake the ’< ’ the first part of the string m_strCache = m_strCache.substring(i);

    //See if the leading text is actually the end of a DOCTYPE tag if ((strText != null) &:& (strText.trim().equals(”]>”))){ / /Create the SVG tag object and return it SVGTag objTag = new SVGTag(”]>”, null); return objTag;

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 72

    } }

    //If this is a CDATA tag, just return that if ((m_strCache.length() > = 9) && (m-strCache.st artsW ith (”

    //G et the tag i = findInC ache(’> ’); if ( i = = —l){return null;} if ((m_strCache.length() >= 9) && (m_strCache.starts With(”

    //ADDED 2/17/06 / /Only maintain leading text if the tag is the end of a text //or grouping element if (!(strFullTag.equals(””) || strFullTag.equals(”< /g> ”))){ if (strText != null){ if (strText.trim().length() == 0){strText = null;} } } ///////////////

    //Create the SVG tag object and return it SVGTag objTag = new SVGTag(strFullTag, strText); return objTag; } catch (Exception e){ System.out.println(e); return null; } } /**

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 73

    * This method looks in the cache of the input file for the char specified . * It will pull from the file until it finds the character or the file * ends. * ©param p_charToFind The character to find * @return The position of the character in the string , or — 1 if not found */ private int findInCache(char p_charToFind){ String strln = null; //Line input from the file int i; //Counting variable

    try { / /Look for the character i = m_strCache.indexOf(p_charToFind); while (i == —1){ / / Add the next line from the file strln = m_brIn.readLine(); if (strln == null){return —1;} if (m.booInCDATA == true){ //In CDATA keep hard returns m_strCache = m-strCache.concat(”\n” + strln); } e ls e { if (strln.trim ().length() != 0){ m_strCache = m_strCache.concat (” J ’ + strln.trim()); } } i = m-strCache.indexOf(p-charToFind); } r e t u r n i; } catch (Exception e){ System .out ,p rin tln (e); r e t u r n —1; } }

    /** * This method writes a line of text to the output SVG file. * @param p_strText The text to write out */ protected void writeLine(String p_strText){ try { m-bwOut .write(p_strText); m_bwOut.newLine(); } catch (Exception e){ System.out.println(e); } } /** * This method reads a line from the input file * ©return Line from the input file */ protected String readLine(){ try { return m_brIn.readLine(); } catch (Exception e){ System.out.println(e); return null; } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 74

    Listing B.6. SVGTag. ja v a im port java.util.Vector;

    * The SVGTag class for handling SVG source information. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class SVGTag { /** * The full data of the tag */ protected String m-strData = null;

    /** * Any text before the tag */ protected String m_strLeadingTxt = null;

    /** * Text before the attributes on the tag */ protected String m_strBeforeAtts = null;

    /** * Text after the attributes on the tag */ protected String m_strAfterAtts = null;

    /** * Any attributes on the tag */ protected Vector m.vAtts = null;

    /** * The constructor for the class . * @param p_strData The full data of the tag * @param pjstrLeadingTxt Any text before the tag */ public SVGTag(String p_strData, String p_strLeadingTxt){ m^strData = p_strData; m_strLeadingTxt — p_strLeadingTxt; } /** * This method gets the full SVG tag. * @return The full SVG tag */ public String getFullTag(){ if (m.vAtts != null){ //A ttributes may have been changed, so update the full tag text m_strData = m_strBeforeAtts; for (int i=0; i < m_vAtts.size(); i++){ m_strData = m_strData.concat(” J’ + m.vAtts.elementAt(i).toString()); } m-strData = m^trData.concat (m_str After Atts);

    //Check if all the attributes have been removed if (m_vAtts.size() = = 0){m_vAtts = null;} } return m jstrD ata; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 75

    /** * This method tells if the tag has leading text. * (©return True if there is leading text */ public boolean hasLeadingTxt(){ return (mjstrLeadingTxt != null); }

    /** * This method gets the leading text before the tag. * ©return The leading text before the tag */ public String getLeadingTxt(){ return m_strLeadingTxt; }

    /** * This method returns true if the tag is an end tag. * @return True if the tag is an end tag */ public boolean isEnd(){ return (m_strData.startsWith(”< /”)) || (m_strData.endsWith(”/> ”)) || (m_strData.startsWith(” ”)) || (m.strData.equals(”]]>” )) || ((m_strData.startsWith(””)));

    /** * This method gets the name of the tag. * @return The name of the tag */ public String getName(){ int intlndex; //Index into the string

    intlndex = m_strData.indexOf(” J’); if (intlndex = = —1){ intlndex = m-strData.indexOf(”>”); if (in tln d ex = = —1){ return m_strData.substring(l); } e ls e { if (m_strData.equals(”]>”)){return ”/!DOCTYPE”;} if (m_strData.equals(”]]>”)){return ”/![CDATA[”;} retu rn m_strData.substring( 1, intlndex); } } e lse { return m_strData.substring(l, intlndex); } } j ** * This method gets the text of the tag before any attributes. * @return Tag text before attributes *i public String getTxtBeforeAtts(){ if (m-vAtts = = null){getAtts();} return m_strBeforeAtts; } j **

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. * This method gets the text of the tag after any attributes. * ©return Tag text after attributes */ public String getTxtAfterAtts(){ if (m-vAtts = = null){getAtts();} return m-strAfterAtts; } /** * This method tells if the tag has attributes . * ©return True if the tag has attributes */ public boolean hasAtts(){ if (m.vAtts = = null){getAtts();} return !(m_vAtts == null); }

    /** * This method gets the attributes on the tag. * ©return List of attributes on the tag */ public Vector getAtts(){ int i; //Counting variable int j = 0; //Counting variable String strAtt; //An attribute name char chQuote; //Character that surrounds an attribute value

    if (m-vAtts == null){ / /Look for attributes i = m _strD ata.indexO f(’= ’); while (i != —1){ / / Get the name of the attribute strAtt = getLastWord(m-strData.substring(j, i)); if (j == 0){ / /On the first attribute, so get the text before them m-strBeforeAtts = m_strData.substring(0, m-strData.lastIndexOf(strAtt,i)). trim(); } j = i;

    / /XML recognizes both ’ and ” to surround attributes //so figure out which one is being used chQuote = getNextQuote(m_strData, i);

    / /Look for the quotes around the attribute value i = m_strData.indexOf(chQuote, i); if (i != —1){ j = i; i = m_strData.indexOf(chQuote, i + 1); }

    / / Create the attribute and add it to the Vector if (i != -1){ if (m.vAtts == null){m_vAtts = new Vector();} m_vAtts.add(new SVG Attribute (strAtt, m_strData.substring(j + 1, i))); j = i + i;

    //Look for another attribute i = m_strData.indexOf(’= ’, j);

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 77

    //Get any text after attributes , if any exist if (( m.vAtts != null) &&; (m_strAfterAtts = = null)){ m_strAfterAtts = m-strData.substring(j); }

    return m.vAtts; }

    * This method returns the last word in a string. * ©param p_str String to pull the last word from * @return The last word in the string */ protected String getLastWord(String p_str){ String newStr = p_str.trim(); int i = newStr.lastIndexOf(’_’); if (i != -1){ newStr = newStr.substring(i + 1); } return newStr;

    /** * This method returns the next quote (’ or ”) in a string. * ©param p_str String to find the quote in * @param p-intStart Index to start looking in the string * (©return The next quote (either a ’ or a ") */ protected char getNextQuote(String p_str, int p_intStart){ char chChar = int i = p-intStart;

    //Look for the next quote while (i < p-str.length()){ chChar = p_str.charAt(i); if ((chChar == || (chChar == ’\”)){return chChar;} i+ + ; } r e tu r n } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 78

    Listing B.7. SVGAttribute.java /* * The SVGAttribute class for specifying an attribute on an svgTag. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class SVGAttribute { /** * The name of the attribute */ protected String m.strName = null; /** * The value of the attribute */ protected String mjstrVal = null; /** * The constructor for the class . * @param p_strName The attribute name * ©param p_strVal The attribute value */ public SVGAttribute(String p-strName, String p_strVal){ m_strName = new String(p-strName); m_strVal = new St.ring(p^strVal); } /** * This method gets the name of the attribute. * ffiireturn The name of the attribute */ public String getName(){ return new String(m_strName); } /** * This method gets the value of the attribute. * ©return The value of the attribute */ public String getValue(){ return new String(m_strVal); } /** * This method sets the value of the attribute. * @param p-StrNewVal The new value the attribute should have */ public void setValue(String p_strNewVal){ m-strVal = new String(p-strNewVal); } /** * This method returns the key for the SVG attribute for sorting and * ordering purposes. * ©return The key for the SVG attribute */ public Integer key(){ return new Integer(m_strName.hashCode()); } /** * This method gets the string representation of the attribute. * @return The string value of the attribute */ public String toString(){ return new String(m_strName + ”= \”” + m_strVal + } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 79

    Listing B.8. TagProcessor. java im port java.io.File; /** * The TagProcessor class for SVG steganographic methods that * embed information in the tags of SVG files. *

    Completed 8/28/04 * ©author Jerry Hungerman */ public class TagProcessor extends LeadTab im plem ents StegMethod { /** * This method manipulates an SVG tag to encode information. Needs to be * overridden by subclass for desired functionality . * @param p_objTag SVG tag to manipulate * ©return The new SVG tag */ protected SVGTag manipTag(SVGTag p.objTag){ //Return the SVG tag with no processing return p_objTag; } /** * This method decodes information from the tags in an SVG file. * ©param p-fileSVG SVG file to extract information from * ©param pJngSeed Random number seed for sample data to embed * ©return True if the embedded information matches what is * generated with the seed passed in */ public boolean decode(File pJileSVG, long pJngSeed) { SVGTag objTag; //An SVG tag

    // Initialization init (p-fileSVG, pJngSeed, null); m.booMatches = true; System.out. print(” Hidden-data: ”);

    //Start processing objTag = m_objTextRW.readTag(); while (objTag != null){ //Process the current tag processTagForDecode(objTag);

    //G et the next tag objTag = m_objTextRW.readTag(); }

    / /Do any final processing on the document finalDecodeProcessing ();

    //Close the input file m.ob jTextRW.closeFiles();

    System.out.println (); System.out.println(” Total: ” + m_infoEmbed.bytesProduced() + ” —bytes” ); return m.booMatches; } /** * This method writes a line of text to the output SVG file using the given * level of indent. * @param p.strText The text to write out

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 80

    * @ par am pjntlndent The current level of indent */ protected void outputLine(String p_strText, int pjntlndent) { String strLead = ” ”; //Leading tabs

    //A dd leading tabs for (int i = 1; i <= pjntlndent; i++){ strLead = strLead.concat(”\t”); }

    //O utput the line m_objTextRW.writeLine(strLead + p_strText); } /** * This method processes an SVG tag from the input file and analyzes it for * hidden information. Needs to be overridden by subclass for desired * functionality. * (Sparam p_objTag The SVG tag to process */ protected void processTagForDecode(SVGTag p.objTag){ m jjooMatches = false; } /** * This method performs any final processing during decode. Needs to be * overridden by subclass for desired functionality . */ protected void finalDecodeProcessing(){ //N o need for implementation in this class }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 81

    Listing B.9. LeadTab.java im port java.io.File; im port java.util.Stack; /** * The LeadTab class for embedding information in SVG files using * leading tabs and spaces on each line of the source file . *

    Completed 8/28/04 * @author Jerry Hungerman */ public class LeadTab extends StegAlgorithm im plem ents StegMethod { /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = T; /** * Tab equivalent number of spaces */ protected static final int NUM_SPACES_TAB = 4; protected static final String TAB_IN_SPACES = ” ____ ”; /** * Object for reading and writing XML text */ protected TextManip m_objTextRW; /** * Stack for keeping track of SVG tags */ protected Stack m_objTags; j ** * A line of output */ protected String m_strOut; /** * Depth of a line of output */ protected int m-intlndent; /** * True if currently processing the CDATA section */ protected boolean m.booInCDATA; /** * This method encodes information in leading spaces and tabs. * Jiparairi p_fileSVG SVG file to embed information into * @param pJngSeed Random number seed for sample data to embed * @return The number of bytes successfully encoded */ public float encode(File p-fileSVG, long pJngSeed){ SVGTag objTag; //An SVG tag

    // Initialization init (p-fileSVG, p-lngSeed, ”_s” + getAbbrev() + ”.svg”);

    //Start processing objTag = m_objTextRW.readTag(); while (objTag != null){ //Process the current tag processTagForEncode(objTag);

    //G et the next tag objTag = m_objTextRW.readTag(); }

    if (mjntlndent != 0){

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 82

    System.out.println(” SVG-not-properly-formed.-Thejautput-file-” + ” reflects -th is); }

    //Close the input and output files m_objTextRW.closeFiles();

    //R eturn the amount of information embedded return mjnfoEmbed.bytesProducedQ; } /** * This method decodes information from the leading spaces and tabs in an * SVG file. * @param p_fileSVG SVG file to extract information from * @param pJngSeed Random number seed for sample data to embed * @return TVue if the embedded information matches what is * generated with the seed passed in */ public boolean decode(File p_fileSVG, long pJngSeed){ String strLine; //A line of the file int i; //Index into the line int intCDATA; //Proper indent for CDATA int intCount; //Indent count for a line

    // Initialization init (p_fileSVG, pJngSeed, null); m.booInCDATA = false; intCDATA = -1; m.booMatches = true; strLine = m.objTextRW.readLine(); System.out.print (” Hidden_data: ”);

    //Process each line for leading tabs/spaces while (strLine != null){ //If CDATA, only get the indents placed by the algorithm if (strLine. trim (). equals (” ”)) { m.booInCDATA = false; intCDATA = — 1; }

    //Process the line for leading tabs/spaces i = 0 ; intC ount = 0; while ((i < strLine.length()) && ((strLine.charAt(i)==’-’) || ( strLine.charAt(i)==’\t’))){ if ((intCDATA == -1) || ((intCDATA != -1) && (intCount <= intCDATA))){ if (strLine.charAt(i)==’\t’){ / /A ta b should be 0 processBit (0); intCount++; i+ + ; } e ls e { / /Spaces should be 1 if (i + NUM.SPACES-TAB <= strLine.length()){ if (strLine.substring(i, i + NUM_SPACES_TAB).equals(TAB_IN_SPACES)){ processB it (1); } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 83

    intCount++; i = i + NUM-SPACES.TAB; } } else {i++;} }

    //If in CDATA, make sure the proper indent count is taken if ((m_booInCDATA = = true) && (intCDATA = = —1)){ intCDATA = intCount; }

    //Get the next line strLine = m_objTextRW.readLine(); }

    //C lo se th e input file m.objTextRW.closeFiles();

    System.out.println (); System.out.println(” Total: ” + mJnfoEmbed.bytesProduced() + ’’-bytes”); return m_booMatches; } j ** * This method gets the abbreviation of the steg method. * ©return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; } /** * This method initializes the LeadTab algorithm. * @param pjileln SVG file for reading * @param pJngSeed Random number seed for gen * @param p-strOutExt String to add on to the original file name for naming * th e o u tp u t file */ protected void init(File pjileln , long pJngSeed, String p_strOutExt) { setupInfoGen(p JngSeed); m_objTextRW = new TextManip(p_fileIn, p_strOutExt); if (p-strOutExt != null){ // Initialize other variables for encode m-objTags = new Stack(); m Jn tln d e n t = 0; m _strO ut = } } /** * This method initializes the information generator. * @param p_lngSeed Random number seed for gen */ protected void setupInfoGen(long pJngSeed) { mJnfoEmbed = new InfoGen(pJngSeed); } / ** * This method processes an SVG tag from the input file and performs data * embedding before output. * @param p_objTag The SVG tag to process */ protected void processTagForEncode(SVGTag p.objTag){ / /Manipulate the tag to encode information (for algorithms using this // class simply for input and output processing) p-objTag = manipTag(p.objTag);

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. if (p.objTag.isEnd() == false){ //O utput the previous start tag if one exists if (m_strOut != ){outputLine(m_strOut, mJntlndent — 1);}

    //Push the current start tag on the stack m.objTags.push(p.objTag.getName()); mjntlndent++;

    / / Cache the current start tag if (p.objTag.hasLeadingTxt()){ m-strOut = p_objTag.getLeadingTxt() + p.objTag.getFullTag(); } else { m_strOut = p.objTag.getFuUTag(); } } else{ //See if the current end tag matches the current start tag if (m.objTags.empty()==false){ if (p-objTag.getName().equals(”/ ” + m-objTags.peek())){ m.objTags.pop(); mJntlndent ; } else { if (m-strOut != ””){ outputLine(m_strOutj mJntlndent — 1); m _strO ut — } } }

    //O utput any leading text and the ending tag if (p-objTag.hasLeadingTxt() = = true){ m-strOut = m_strOut.concat(p_objTag.getLeadingTxt() + P-objTag.getPullTag()); } else { m_strOut = m_strOut.concat(p_objTag.getPullTag()); } if (p-objTag.getName() .equals(” /! [CDATA[”)) { outputCDATA(m_strOut, mJntlndent); } else { outputLine(m_strOut, mJntlndent); }

    // Reinitialize for next tag m _strO ut = } } /** * This method manipulates an SVG tag to encode information (here for * algorithms using this class simply for input and output processing) * Qparam p_objTag SVG tag to manipulate * @return The new SVG tag */ protected SVGTag manipTag(SVGTag p_objTag){ return p-objTag; } /** * This method writes a line of text to the output SVG file using the given * level of indent. * @param p.strText The text to write out

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 85

    * @ par am pjntlndent The level of indent to use */ protected void outputLine(String p-StrText, int pjntlndent){ int i; //Counting variable String strLead = //Leading tabs/spaces

    //Add leading tabs/spaces this is where information is encoded by //this algorithm for (i =1; i <= pjntlndent; i++){ if (mJnfoEmbed.nextQ == 0){ strLead = strLead.concat(” \t”); } e ls e { / / Add a tab equivalent number of spaces strLead = strLead.concat(TABJN-SPACES); } }

    //O utput the line m_objTextRW.writeLine(strLead + p_strText); } /** * This method writes the CDATA section to the output SVG file using the * given level of indent. * @paxam p-strText The text to write out * @param pjntlndent The current level of indent */ protected void outputCDATA(String p_strText, int pjntlndent) { String strLine; //Line of CDATA text int i; //Index of EOLs within the string

    // Initialize m_booInCDATA = true;

    //G et each line of the CDATA i = p-strT ext. indexO f(” \ n ” ); while ((i != —1) || ( p-strText.length() != 0)){ if (i — -1){ / /Trimming each line of CDATA, but could do better strLine = p_strText.trim(); p_strText = } e ls e { / /Trimming each line of CDATA, but could do better strLine = p-strText.substring (0, i).trim (); P-strText = p_strText.substring(i + 1); }

    //O utput the line if ((strLine.startsW ith(””))){ outputLine(strLine, pjntlndent); } else { outputLine(strLine, pjntlndent + 1); }

    //G et the next line i = p-strText.indexOf(”\n”); }

    m.booInCDATA = false; } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 86

    Listing B.10. LeadTabSqOff . java im port java.io.File; im port java.util.HashMap; im port java.util.Vector; /** * The LeadTabSqOff class for embedding information in SVG files * using leading tabs and spaces on each line of the source file . This * algorithm adds a limit to the length of a line output by the * LeadTab algorithm, making for potentially more lines to embed * information into and effectively ’ squaring off ’ or wrapping the text of * the source. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class LeadTabSqOff extends LeadTab im plem ents StegMethod { /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = ’s’;

    /** * Specifies the line limit in characters */ public static final int NUM-CHARS = 80; /** * A map of characters where a line can be broken */ protected static HashMap mJimBreakChars;

    /** * A map of SVG attributes where the line can be broken on numbers */ protected static HashMap m_hmNumsCanBreak; /** * A map of characters that are part of a number */ protected static HashMap mJimNum;

    j ** * Current tag cached for output */ protected SVGTag m_objCurrTag = null;

    /** * This method is a static initializer for the class . It will fill up one * hash map with the characters on which a line can be broken, one with * the SVG attributes where the line can be broken on numbers, and another * with the characters that are part of a number. */ s ta tic { //Create the hash maps m_hmBreakChars = new HashMap(4); m_hmNumsCanBreak = new HashMap(lO); m-hmNum = new HashMap(13); / / Characters that a line can be broken on mJimBreakChars.put(” J ’, null); mJimBreakChars.put(”,”, null); mJimBreakChars.put(”;”, null); mJimBreakChars.put (” = ”, n u ll);

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 87

    mJimBreakChars.put(”>”, null); //Attributes with long listings of numbers mJimNumsCanBreak.put(”d”, null); mJimNumsCanBreak.put(”points”, null); / /Parts of a number m Jim N u m .p u t(” .” , null); mJimNum.put(”0”, null); mJimNum.put(”l”, null); mJimNum.put(”2”, null); mJimNum.put(”3”, null); mJimNum.put(”4”, null); mJimNum.put(”5”, null); mJimNum.put(”6”, null); mJunNum.put(”7”, null); mJimNum.put (” 8” , null); mJimNum.put(”9”, null); } / ** * This method gets the abbreviation of the steg method * @return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; } /** * This method processes an SVG tag from the input file and performs data * embedding before output. * @param p.objTag The SVG tag to process */ protected void processTagForEncode(SVGTag p_objTag){ if (p-objTag.isEnd() = = false) { / / Output the previous start tag if one exists if (m_objCurrTag != null){ handleTags(null, m_objCurrTag, mJntlndent — 1); }

    //Push the current start tag on the stack m_objTags.push(p-objTag.getName()); mjntlndent++;

    //Cache the current start tag m-objCurrTag = p-objTag; m-strOut = p-objTag.getPullTag(); } else{ //See if the current end tag matches the current start tag if (m-objTags.empty()==false){ if (p-objTag.getName().equals(”/ ” + m.objTags.peek())){ m-objTags.pop(); mJntlndent ; } else { if (m-objCurrTag != null){ handleTags(null, m-objCurrTag, mJntlndent — 1); m-objCurrTag = null; } } }

    / / Output any leading text and the tag if (p-objTag.getName() .equals(”/! [CDATA[”)) { //G et the entire CDATA section

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 88

    if (p_objTag.hasLeadingTxt() == true){ m-strOut = m_strOut.concat(p_objTag.getLeadingTxt() + P-objTag.getFullTag()); } else { m-strOut = m_strOut.concat(p_objTag.getPullTag()); } outputCDATA(m_strOut, mJntlndent); } else { handleTags(m_objCurrTag, p_objTag, mJntlndent); }

    / / Reinitialize for next tag m_objCurrTag = null; m jstrO u t = } }

    * This method will write a one or two tags to the output SVG file. * ©param p_objStartTag Corresponding SVG start tag (null if not on same * line) * @param p_objTag SVG tag to output * @param p_intlndent The level of indent to use */ protected void handleTags(SVGTag p-objStartTag, SVGTag p-objTag, int pjntlndent) { String strLead = //Text to be put on the line before a tag int intAdj; //Adjustment in string for leading tabs/spaces String strRest; //Text to be output at the end

    //Set up the adjustment according to the desired indent intAdj = pjntlndent * NUM_SPACBS.TAB;

    // Output the corresponding start tag if it exists if (p-objStartTag != null){ strLead = p_objStartTag.getFullTag(); //If the entire start tag does not fit on a line , output whatever //w ill fill a line (or more) and return what is left over if (strLeadJength() + intAdj > NUM.CHARS){ strLead = outputTag(””, p-objStartTag, pjntlndent); } }

    //Output the tag strRest = outputTag(strLead, p_objTag, pjntlndent); if (strRest.equals(””) == false){ outputLine(strRest, pjntlndent); }

    /** * This method will output a tag taking into account the desired linewidth. * ©param p_objTag SVG tag to output * @ par am p-StrLead Text to be put on the line before a tag * @param pjntlndent The level of indent to use * @return Last portion of the tag that is under the linewidth and has not * been output */ protected String outputTag(String p_strLead, SVGTag p-objTag, int pJntIndent){ String strOut; //Output text SVGAttribute objAtt = null; //A n attribute

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 89

    //Build the leading text for output strOut = p_strLead; if (p-objTag.hasLeadingTxt()){ strOut = strOut.concat(p-objTag.getLeadingTxtQ); }

    //O utput the tag if (p-objTag.hasAtts()){ / /Build the tag and output everything before attributes strOut = strOut.concat(p-objTag.getTxtBeforeAtts()); strOut = wrapOutput(strOut, pjntlndent, false);

    //Build on an attribute at a time and then output Vector vAtts = p.objTag.getAtts(); for (int i=0; i < vAtts.size (); i++){ objAtt = (SVGAttribute)vAtts.elementAt(i); if (mJimNumsCanBreak.containsKey(objAtt.getName())){ //A dd the name of the attribute strOut = strOut.concat(”J ’ + objAtt.getName() + ”= \””); strOut = wrapOutput(strOut, pjntlndent, false);

    / / Add the value of the attribute — can break on numbers //w ith in strOut = strOut.concat(objAtt.getValue() + ”\””); strOut = wrapOutput(strOut, pjntlndent, true); } else { strOut = strOut.concat(”J ’ + objAtt.toString()); strOut = wrapOutput(strOut, pjntlndent, false); } }

    //Build on the end of the tag and then output strOut = wrapOutput(strOut.concat(p.objTag.getTxtAfterAtts()), p jn tln d e n t, f a ls e ); } else { //Finish building the tag for output strOut = strOut.concat(p_objTag.getFullTag());

    //O utput the tag strOut = wrapOutput(strOut, pjntlndent, false); }

    return new String(strOut); } /** * This method writes a line of text to the output SVG file using the given * level of indent, wrapping lines to keep a width at or below NUM-CHARS * characters. * @param pjstrln The text to write out * @param pjntlndent The current level of indent * dpararn p.booNums True if output can be wrapped immediately before a * num ber */ protected String wrapOutput(String p_strln, int pjntlndent, boolean p.booNums) { int intAdj; //Adjustment in string for leading tabs/spaces int intBreak = — 1; //W here the text has to be broken String strLeft = ” ”; //What is left that is under the line limit

    //Set up the adjustment according to the desired indent

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 90

    intAdj = pjntlndent * NUM-SPACES.TAB;

    //If the text is over NUM.CHARS characters, then wrap it if ((p-strln .length() + intAdj) > NUM.CHARS){ / /Figure out where the line has to break if (p_booNums){intBreak = getBreakWithNums(p_strIn, intAdj);} else {intBreak = get Break (p-strln, intAdj);}

    i f (intB reak = = —1){ //N o break could be found so just write the whole line outputLine(p.strIn, pjntlndent); } e ls e { //W rite out the current line outputLine(p_strIn.substring(0, intBreak 4- 1), pjntlndent); / /Process the next line strLeft = wrapOutput(” + p-strln .substring(intBreak + l).trim(), pjntlndent, p.booNums); } } e ls e { if ((p_strln.trim().length() + intAdj) = = NUM_CHARS){ outputLine(p_strIn.trim (), pjntlndent); } e ls e { strLeft = p_strln; } }

    return new String(strLeft); } /** * This method will find the proper character to break on in a string. * @param p-strln String to look at * @param pJntAdj Adjustment in where to look at the string (for leading tabs) * @return Index at which the string can be split (—1 if nowhere) */ protected int getBreak(String p_strln, int pJntAdj){ int intPt; //The break point to return

    intPt = (NUM-CHARS — pJntAdj) — 1;

    //Look for the spot to break the line on while ((intPt >= 0) && (m_hmBreakChars.containsKey( p-strln.substring(intPt, intPt + 1)) == false))} in tP t ; } if (intPt >= 0){ if (p-strln.charAt(intPt) == V){intPt ;} }

    //If the line cannot break anywhere before the initial point, go the //other way if (intPt < 0) { // Initialize start point again intPt = NUM-CHARS — pJntAdj; if (intPt < 0){intPt = 1;} //C an have a leading space

    while (intPt < p_strln.length() — 1){ if (m_hmBreakChars.containsKey( p-strln .substring(intPt, intPt + !))){

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 91

    if (p_strln .charAt(intPt) == ’J){intPt ;} return intPt; } in tP t+ + ; } if (intPt >= (p-strln.length() — l)){return —1;} }

    return intPt; } /** * This method will find the proper character to break on in a string that * has a list of numbers. * @param p_strln String to look at * @param pJntAdj Adjustment in where to look at the string (for leading tabs) * ©return Index at which the string can be split (—1 if nowhere) */ protected int getBreakWithNums(String p-strln, int pJntAdj){ int intPt; //The break point to return

    intPt = (NUM.CHARS - pJntAdj) - 1;

    //Look for the spot to break the line on while ((intPt >= 0) && (mJimNum.containsKey( p-strln. substring (intPt, intPt + 1)))){ in tP t ; } if (intPt >= 0){ if ((p_strln .charAt(intPt) == ’_’) || (p_strln .charAt(intPt) = = ’—’)){ in tP t ; if ((intPt == 0) SzSz (p_strIn.charAt(l) = = ’—’)){ //If we don’t do this, we’ll get into an infinite loop intPt = getBreakWithNums(p_strIn.substring(2), pJntAdj); if (in tP t < 0 ){intP t = —1;} else {return intPt + 2;} } } }

    //If the line cannot break anywhere before the initial point, go the / / other way if (intPt < 0) { // Initialize start point again intPt = NUM.CHARS - pJntAdj; if (intPt < 0){intPt = 2;} //C an have a leading space & negative

    while (intPt < p_strln.length() — 1){ if (m_hmNum.containsKey( p-strln .substring(intPt, intPt + 1)) == false){ if ((p-strln .charAt(intPt) == ’. ’) || (p-strln .charAt(intPt) == ’—’)){intPt ;} return intPt; } in tP t+ + ; } if (intPt >= (p_strln.length() — l)){return —1;} }

    return intPt; } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 92

    Listing B .ll. TweakNums.java im port java.io.File; im port java.util.Vector; im port java.util.HashMap; im port java.math.BigDecimal; /** * The TweakNums class for embedding information in SVG files * using the least significant digit of numbers specified in the source file . *

    Completed 8/28/04 *

    Update History: * < u l> *

  • 4/23/06: Added method {@link #getSigDigs(String) getSigDigs} * to the number of significant digits in the number passed in.
  • *
  • 3/12/06: Adjusted method {@link NumEncoder#tweakNum(String) * NumEncoder.tweakNum} to calculate Root Mean Square (RMS) Error.
  • *
  • 3/3/06: Added printing of analysis of the algorithm through * adjusting the method {@link NumEncoder#tweakNum(String) * NumEncoder.tweakNum} and adding the method {@link #printAlgInfo * printAlglnfo} and surrounding code.
  • * < /u l> * @author Jerry Hungerman */ public class TweakNums extends TagProcessor im plem ents StegMethod { /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = V;

    /** * The number of significant digits required for a number to qualify for * tweaking */ protected static final int NUM_SIG_DIGITS = 4;

    /** * A map of all the SVG attributes this algorithm potentially manipulates */ protected static HashMap m_hmAttsToManip;

    /** * Object that hides information in the numbers in a string */ protected NumEncoder m_neln = new NumEncoder(); /** * Object that retrieves information from the numbers in a string */ protected NumDecoder m_ndOut = new NumDecoder();

    /** * Number of bytes embedded by the algorithm */ protected float m_fltBytesEmbedded; / ** * Sum of the absolute value of the difference from original to tweaked for * all numbers adjusted by the algorithm */ protected double m.dblErrSum; /**

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 93

    * The maximum error found between the original and tweaked numbers *1 protected double m-dblMaxErr;

    /** * Total number of values tweaked by the algorithm */ protected int m_intTweakCount;

    /** * Number of significant digits to display for error calculations . * Initialized to be high and minimized by the code. */ protected int m JntA ccuracy = 10; /** * This method is a static initializer for the class . It will fill up a * hash map with the SVG attributes that we are looking for. */ static { / / Create the hash map with all the attribute names for quick lookup mJimAttsToManip = new HashMap(27);

    //Typical positioning attributes m JimAttsToM anip.put(”x” ,null); m -hmAttsToManip.put(”y” ,null); m JimAttsToM anip.put(”xl ” ,null); mJimAttsToManip. put (”y l” ,null); m Jim AttsToM anip.put (” x2”, null); mJimAttsToManip.put (” y2”, null); m Jim AttsToM anip.put (” cx”, null); m Jim AttsToM anip.put (” cy” ,null); m Jim AttsToM anip.put (” dx” .null); m Jim AttsToM anip.put (” dy”, null) ; mJimAttsToManip.put(”fx” .null); mJimAttsToManip.put(” fy” .null); mJimAttsToManip.put(” rx” .null); mJimAttsToManip.put(” ry” .null); mJimAttsToManip.put(” r” .null);

    //Height and width attributes mJimAttsToManip.put(” width”, null); mJimAttsToManip.put(” height” .null);

    //Polygon and Polyline data mJimAttsToManip.put(”points”,null);

    //Path and Glyph element data mJimAttsToManip.put(”d” .null);

    //Transform data mJimAttsToManip.put(” transform”, null);

    //Where text starts on a path mJimAttsToManip.put(”start Offset”, null);

    //Animation elements mJimAttsToManip.putf’to” .null); mJimAttsToManip.put(” values”, null); mJimAttsToManip.put(”keyTimes” .null); mJimAttsToManip.put(”keyPoints”, null); mJimAttsToM anip. put (” dur”, null);

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 94

    /** * This method allows for the use of other number encoders/decoders by * subclasses. * @param e NumEncoder to use * @param d NumDecoder to use */ protected void insertNumCoders(NumEncoder e, NumDecoder d){ m _neln = e; m_ndOut = d; } /** * This method prints to the standard output information about the * parameters of the algorithm. * @param pJntDig The number of significant digits required for tweaking * @param pJntDecDig The number of decimal digits required for tweaking * @param p-intBits The number of bits embedded per tweaked number (if —1 * is passed, the value is calculated) */ public void printAlgInfo(int pJntDig, int pJntDecDig, int p_intBits){ System.out.println(” ------”); System.out.println(” Digits\tDecDig\tTotal\tBits\tM axErr\tRM S”); System.out.print(p jntDig + ”\t” + pJntDecDig + ”\t” + m JntT w eakC ount + ” \ t ” ); if ((pJntBits == —1) && (mJntTweakCount != 0)){ System.out.print (precRounded(mJitBytesEmbedded/mJntTweakCount*8));} else {System.out. print (pJntB its);} if (mJntTweakCount == 0){ System.out.println(” \tO\tO”); } e ls e { System.out.println(”\t” + precRounded(m_dblMaxErr) + ”\t” + precRounded(Math.sqrt(m_dblErrSum/mJntTweakCount))); } System.out.println(” ------”); } /** * This method rounds the given double to the precision of four digits * beyond the decimal point. * @param p-dblln double to round * @return The rounded double */ protected double precRounded.OLD (double p.dblln){ long IngDiv; //Power of 10 for the calculation

    IngDiv = new Double(Math.pow(10, 5)).longValue(); return (double) (Math.round(p.dblIn * lngDiv))/lngDiv; } /** * This method rounds the given double to the precision of the numbers used * in calculating error. * @param p-dblln double to round * @return The rounded double */ protected String precRounded(double p_dblln){ int i = 0; //For calculation of decimal point BigDecimal bdNum; //Accurately represents significant digits

    if (p-dblln == 0) { bdNum = new BigDecimal(p_dblIn); } e ls e {

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 95

    / /Get the log base 10 of the number, then round down to nearest int //for decimal point placement i = (int)Math.floor(Math.log(p-dblln) / Math.log(lO));

    / /Move the decimal point and set the number of significant digits if ( i < 0){bdNum = new BigDecimal(p.dblIn).movePointRight(—1 * i);} else {bdNum = new BigDecimal(p_dblIn).movePointLeft(i);} }

    //Set the number to the proper significant digits bdNum = bdNum.setScale(mJntAccuracy — 1, BigDecimal.ROUND_HALF_UP); if ( i < 0){return bdNum.movePointLeft(—1 * i).toString();} else {return bdNum.movePointRight(i).toString();} }

    /** * This method gets the number of significant digits in the number passed * in. * @param p_strln number to evaluate * ©return The number of significant digits in the parameter */ protected int getSigDigs(String p_strln){ double dblNum; //Number to evaluate int i; //For calculation of decimal point BigDecimal bdNum; //Accurately represents significant digits

    / / Check for the empty string or null if (p-strln.equals(”” ) || p_strln == null) return 0;

    / /W ant to take the log of only positive numbers dblNum = Math.abs(Double.parseDouble(p_strIn)); if (dblNum == 0) return 1;

    //G et the log base 10 of the number, then round down to nearest int for //decimal point placement (erroneous at certain boundaries, e.g., 1000) i = (int)Math.floor(Math.log(dblNum) / Math.log(lO));

    / /Move the decimal point according to the log if ( i < 0) {bdNum = new BigDecimal(p-strIn).movePointRight(—1 * i);} else {bdNum = new BigDecimal(p_strIn).movePointLeft(i);}

    //R eturn the number of significant digits if (bdNum.compareTo(new BigDecimal(lO)) >= 0){ //If our log calculation improperly rounded, return the proper value return bdNum.scale() + 2; } return bdNum.scale() + 1;

    /** * This method inserts the printing of algorithm analysis after calling * the encode method in the superclass. * @param pJileSVG SVG file to embed information into * @ par am pJngSeed Random number seed for sample data to embed * @return The number of bytes successfully encoded */ public float encode(File p_fileSVG, long pJngSeed){ //Call the actual encode m-fltBytesEmbedded = super.encode(p-fileSVG, pJngSeed);

    / /Print out information about the algorithm printAlgInfo(NUM_SIG_DIGITS, 0, 3);

    //Return the final results

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 96

    return m_fltBytesEmbedded; } /** * This method manipulates an SVG tag to encode information using the least * significant digit of numbers specified in its attributes. * @param p_objTag SVG tag to manipulate * ©return The new SVG tag */ protected SVGTag manipTag(SVGTag p.objTag){ Vector vAtts = p-objTag.getAtts(); //A ny attributes on the tag SVGAttribute objAtt; //An attribute on the tag

    if (v A tts != null){ for (int i = 0; i < vAtts.size (); i++){ //Figure out if the tag has attributes to tweak objAtt = (SVGAttribute)vAtts.elementAt(i); if (mJimAttsToManip.containsKey(objAtt.getName())){ //Tweak any numbers in the attribute objAtt.setValue(m.neIn.scanStr(objAtt.getValue())); } } }

    //Return the modified SVG tag return p.objT ag; } /** * This method gets the abbreviation of the steg method. * @return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; } /** * This method processes an SVG tag from the input file and analyzes it for * hidden information. * @param p.objTag The SVG tag to process */ protected void processTagForDecode(SVGTag p.objTag){ Vector vAtts = p-objTag.getAtts(); //A ny attributes on the tag SVGAttribute objAtt; //An attribute on the tag if (v A tts != null){ for (int i = 0; i < vAtts.size (); i++){ //Figure out if the tag has attributes to read objAtt = (SVGAttribute)vAtts.elementAt(i); if (mJimAttsToManip.containsKey(objAtt.getName())){ //Extract information from any numbers in the attribute m .ndOut .scanStr(obj Att .getValue()); } } }

    /** * Class for hiding information in numbers that reside in a string. */ protected class NumEncoder { /** * This method will scan a string, parsing out any numbers and * potentially manipulating them to hide information.

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 97

    * @param p-strln String to scan * ©return String with numbers potentially modified */ protected String scanStr(String p_strln){ String 0 numbers; //Array of numbers in the attribute String [] nonNumbers; //O ther characters in the attribute String strNew; //Modified string int i = 0; //Counting variable int j = 0; //Counting variable

    //G et the numbers in the string (DOES NOT GET THE NEGATIVE SIGN) numbers = p_strln.split(” [\\D&&[“\\.]]+”);

    //G et the other characters in between the numbers in the string nonNumbers = p-strln. split (”\\d+ (\\.\\d+ )?+ ”);

    //S et the new string by tweaking the numbers in it strN ew = new String(); while ((i < numbers.length) || (j < nonNumbers.length)){ if ((p-strln .length() > 0) && (p-strIn.substring (0,1). matches(”\\d ”))){ //If the string begins with a number, we start with the //nonN umbers if (j < nonNumbers.length){ strNew = strNew. concat(nonNumbers[j]); } if (i < numbers.length){ //A dd the potentially adjusted number to the new string strNew = strNew.concat(scanNum(numbers[i])); } } else { //If the string begins with a nonNumber, we start with the / /num bers if (i < numbers.length){ //Add the potentially adjusted number to the new string strNew = strNew.concat(scanNum(numbers[i])); } if (j < nonNumbers.length){ strNew = strNew.concat(nonNumbers[j]); } } i+ + ; j + + ; > return strNew; } /** * This method will scan a number, hiding information in it if it has * at least NUM-SIGJDIGITS significant digits. * @param p_strln Number to scan * @return String with number potentially modified */ protected String scanNum(String p_strln){ int intSigDigs; //Number of significant digits in the string

    //Check the number of significant digits in the string intSigDigs = getSigDigs(p-strln); if (intSigDigs >= NUM.SIG.DIGITS){ //Adjust for error calculations if (intSigDigs < mJntAccuracy){mJntAccuracy = intSigDigs;}

    //Adjust the least significant digit

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. return tweakNum(p_strIn); } return p_strln; } /** * This method will hide information in a number by changing its least * significant digit. * @param p_strln Number to adjust * @return String with number modified */ protected String tweakNum(String p_strln){ String strResult; //The result of tweaking double dblErr; /*Absolute value of the difference between original and tweaked divided by the original * /

    //Tweak the number strResult = p-strln.substring (0, p_strln .length() — 1) + newDigit();

    //Calculate the error dblErr = Math.abs((Double.parseDouble(strResult) — Double.parseDouble(p_strIn))/Double.parseDouble(p_strIn)); if (m_dblMaxErr < dblErr){m_dblMaxErr = dblErr;}

    //Summing the error caused by tweaking squared to calculate root //m ean square error m_dblErrSum + = Math.pow(dblErr, 2);

    / /Increment the tweaked numbers count &: return the tweaked number m_intTweakCount++; return strR esult; } /** * This method will create an octal digit to be placed in the least * significant digit of a number in the SVG file. * @return String containing the digit */ protected String newDigit(){ int theDigit = mJnfoEmbed.next() * 4; theDigit = theDigit + m_infoEmbed.next() * 2; theDigit = theDigit + m_infoEmbed.next(); return Integer.toString(theDigit); } }

    * Class for retrieving information from numbers that reside in a string. */ protected class NumDecoder extends NumEncoder { /** * This method will retrieve information from a number by looking at * its least significant digit. * @param p_strln Number to look at * @return String with the number (just here because of NumEncoder) */ protected String tweakNum(String p_strln){ //G et the last digit and extract information decodeData( Integer. parselnt (p-strln .substring(p_strln .length () — 1))); //Just return the string with no processing

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 99

    return p_strln;

    * This method will retrieve information from a number by converting * it from octal to binary. * @param pJntDigit Number to translate */ protected void decodeData(int pJntDigit){ / /Make sure the number is an octal digit if (pJntDigit > 7){ System.out.println (); System.out.println(” OctaL-digit ~not-found.”); } else { / /Convert the number back to binary and process each bit if (pJntDigit >= 4){ processBit(l); pJntDigit = pJntDigit — 4; } else {processBit(O);} if (pJntDigit >= 2){ processBit(l); pJntDigit = pJntDigit — 2; } else {processBit(O);} if (pJntDigit >= 1){ processBit(l); pJntDigit = pJntDigit — 1; } else {processBit(O);} } } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 100

    Listing B.12. TweakTwoNums. java / ** * The TweakTwoNums class for embedding information in SVG files * using two of the least significant digits of numbers specified in the * source file . If there are not at least two digits beyond the decimal point, * then this algorithm will only embed information in the least significant * d ig it. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class TweakTwoNums extends TweakNums im plem ents StegMethod { /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = ’w’; /** * This method gets the abbreviation of the steg method. * ©return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; } /** * The constructor for the class. */ public TweakTwoNums(){ //Have the superclass use the Number encoder and decoder implemented //in this class insertNumCoders(new NumEncoderQ, new NumDecoderQ); } /** * This method prints to the standard output information about the * parameters of the algorithm. * @param pJntDig The number of significant digits required for tweaking * @ par am pJntDecDig The number of decimal digits required for tweaking * ©param pJntBits The number of bits embedded per tweaked number */ public void printAlgInfo(int pJntDig, int pJntDecDig, int pJntBits){ super.printAlgInfo(pJntDig, 2, —1); >

    * This method will evalute a number to see if it has at legist two * digits beyond the decimal point. * @param p-strln Number to evaluate * ©return True if the number has at least two digits beyond the * decimal point */ protected boolean hasTwoBeyondDecPoint(String p_strln){ if (p_strln .indexOf(” ) != —1){ if (p-strln .lengthQ — p-strln.indexOf(” ) > 2){ return true; } } return false; } /** * Class for hiding information in numbers that reside in a string. */ protected class NumEncoder extends TweakNums.NumEncoder { j ** * This method will hide information in a number by changing its least

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 101

    * significant digit . It will change two of the least significant * digits if the number has at least two digits beyond the decimal point. * @param p-strln Number to adjust * @return String with number modified */ protected String tweakNum(String p_strln){ String strResult; //The result of tweaking double dblErr; /*Absolute value of the difference between original and tweaked divided by the original */ / /Tweak the number if ( hasTwoBeyondDecPoint (p_strln)) { //Tweak two of the least significant figures strResult = p-strIn. substring (0, p_strln .length() — 2) + newDigitQ + newDigitQ; } e ls e { //Tweak the least significant figure strResult = p_strln. substring (0, p_strln ,length() — 1) + newDigit(); }

    //Calculate the error dblErr = Math.abs((Double.parseDouble(strResult) — Double.parseDouble(p-strln))/Double.parseDouble(p-strln)); if(m_dblMaxErr < dblErr){m_dblMaxErr = dblErr;}

    / /Summing the error caused by tweaking squared to calculate root / / mean square error m.dblErrSum + = Math.pow(dblErr, 2);

    / /Increment the tweaked numbers count & return the tweaked number m_intTweakCount++; return strResult; } } /** * Class for retrieving information from numbers that reside in a string. */ protected class NumDecoder extends TweakNums.NumDecoder { /** * This method will retrieve information from a number by looking at * its two least significant digits . * @param p-strln Number to look at * @return String with the number (just here because of NumEncoder) */ protected String tweakNum(String p_strln) { int len = p_strln.length (); if (hasTwoBeyondDecPoint (p-strln)) { / / Get the second to last digit and extract information decodeData( Integer.parselnt(pjstrln.substring(len — 2, len — 1))); } //G et the last digit and extract information decodeData(Integer.parseInt(p_strIn. substring (len — 1)));

    //Just return the string with no processing return p_strln;

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 102

    Listing B.13. TweakTwoNumsMinErr. j ava im port java.io.File;

    * The TweakTwoNumsMinErr class for embedding information in SVG * files using two of the least significant digits of numbers specified in the * source file . This class uses an algorithm that treats the two least * significant figures as a number in the set (0,99), dividing it into * intervals of 2“(number of bits being embedded per number) and embedding * 1 — 6 bits in the interval closest to the original value. *

    Completed 3/3/06 *

    Update History: * < u l> *

  • 3/12/06: Adjusted method {@link NumEncoder#tweakNum(String) * NumEncoder.tweakNum} to calculate Root Mean Square (RMS) Error.
  • * < /u l> * ©author Jerry Hungerman */ public class TweakTwoNumsMinErr extends TweakNums im plem ents StegMethod { /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = ’m’;

    /** * The number of significant digits required for a number to qualify for * tweaking (must be 2 or greater) */ protected static final int NUM_SIG_DIGITS = 4;

    /** * Tweak numbers with only this many digits beyond the decimal point */ protected static final int NUM_DIGITS_BEYOND_DEC = 0;

    /** * Number of bits to hide in each number (since we’re looking at the two * LSFs, this value can range from 1 to 6) */ protected static final int NUM-BITS = 2;

    /** * Substitution Table for embedding */ protected int[][] m_arrEmbedTable;

    * This method gets the abbreviation of the steg method. * ©return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; }

    /** * This method gets the number of significant digits required for tweaking * ({©link #NUM_SIG-DIGITS NUM-SIG-DIGITS}). * ©return The number of digits required for tweaking */ protected int getNumDigits(){ return NUM.SIG_DIGITS; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 103

    /** * This method gets the number of digits required beyond the decimal point * for tweaking ({©link #NUM_DIGITS_BEYOND_DEC NUM_DIGITS_BEYONDJDEC}). * @return The number of digits required beyond the decimal point for * tweaking */ protected int getNumDigitsBeyondDec(){ return NUM JDIGITS J3EYOND_DEC; }

    /** * This method gets the size of the interval over which data is embedded. * @return Size of the interval over which data is embedded */ protected int getIntervalSize(){ return new Double(Math.pow(2, (NUM_BITS))).intValue(); }

    /** * The constructor for the class . */ public TweakTwoNumsMinErr(){ m_dblErrSum = 0.0; mJntTweakCount = 0; //Have the superclass use the Number encoder and decoder implemented //in this class insertNumCoders(new NumEncoder(), new NumDecoder()); }

    /** * This method prints to the standard output information about the * parameters of the algorithm. * @param pJntDig The number of significant digits required for tweaking * ©param pJntDecDig The number of decimal digits required for tweaking * ©param pJntBits The number of bits embedded per tweaked number */ public void printAlgInfo(int pJntDig, int pJntDecDig, int pJntBits){ super.printAlgInfo(getNumDigits(), getNumDigitsBeyondDecQ, NUM_BITS); }

    /** * This method builds the substitution table for embedding. */ protected void buildEmbedTable(){ int intBase; //The interval size

    / / Initialize intBase = getlntervalSize ();

    // Initialize the array m-arrEmbedTable = new int[100][intBase];

    fo r ( in t i = 0; i < 100; i+ + ){ for (int j = 0; j < intBase; j++){ if ((intBase == 64) Sc&c (i > 63) &:& (j > 35)){ m_arrEmbedTable[i][j] = j; } else if ((i > 95) &;& (j > 3)){ m_arrEmbedTable[i][j] = (intBase * (95 / intBase)) + j; } e ls e { m_arrEmbedTable[i][j] = (intBase * (i / intBase)) + j; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 104

    } } } /** * This method inserts the building of the embedding table before encoding * begins and then calls the encode method in the superclass. * @param p_fileSVG SVG file to embed information into * @param pJngSeed Random number seed for sample data to embed * ©return The number of bytes successfully encoded */ public float encode(File pJileSVG, long pJngSeed){ //Build the substitution table for embedding buildEmbedTable ();

    //Call the actual encode return super.encode(p.fileSVG, pJngSeed); } /** * This method will evalute a number to see if it meets the requirements * for tweaking, having at least {@link #getNumDigits() getNumDigits} * digits as well as {@link #getNumDigitsBeyondDec() getNumDigitsBeyondDec} * digits beyond the decimal point. * ©par am p_strln Number to evaluate * ©return True if the number meets all requirements for tweaking */ protected boolean meetsReqs(String p_strln){ int intSigDigs; //Number of significant digits in the string int intDecLoc; //Location of the decimal point int intDigBeyondDec; /*Required number of digits beyond the decimal p o in t* /

    // Initialize intSigDigs = getSigDigs(p_strIn); intDecLoc = p-strIn.indexOf(”.”);

    //Check the number of significant digits if (intSigDigs > = getNumDigits()){ intDigBeyondDec = getNumDigitsBeyondDec();

    //If there’s no decimal requirement, then return true if (intDigBeyondDec < 1){ //A djust for error calculations if (intSigDigs < m.intAccuracy){m.intAccuracy = intSigDigs;}

    return true; }

    //Check for the decimal point and the requirement if (intDecLoc != —1){ if ( p-strln .length() — intDecLoc > intDigBeyondDec)} //Adjust for error calculations if (intSigDigs < m jn t Accuracy) {m_intAccuracy = intSigDigs;}

    return true; } } } //D oesn’t meet all requirements return false;

    /**

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 105

    * This method will hide information in a number by substituting the two * least significant digits with the entry in the embedding table * corresponding to the original number and the bits to be embedded. * C'iparam p_strln Number to adjust * ©return String with number modified */ protected String embedInNum(String p_strln){ int intLen; //Length of the string containing the number boolean booDec; / *True if the two least significant digits have a decimal between them*/ int intOrig; //Original two least significant digits String strNew; //New two least significant digits

    / / Initialize intLen = p_strln.length (); booDec = hasDecBetweenTwoLSFs(p_strIn);

    //G et the original two least significant figures intOrig = getTwoLSFs(p_strIn, booDec);

    / / Get the new two least significant figures & add them to the new //n u m b e r strNew = Integer.toString( m_arrEmbedTable[intOrig][mJnfoEmbed.nextBits(NUM_BITS)]); if (booDec == true){ if (strNew.lengthQ == l){strNew = ”0.” + strNew;} else {strNew = strNew.substring(0,l) + + strNew.substring(l,2);} strNew = p_strln.substring (0, intLen — 3) + strNew; } e ls e { if (strNew.length() == l){strNew = ”0” + strNew;} strNew = p_strln.substring (0, intLen — 2) + strNew; }

    / /Return the result return strNew; } /** * This method will tell if the two least significant digits of a number * have a decimal between them. * @param p-strln Number to look at * @return True if a decimal exists between the two least significant * figures of the input */ protected boolean hasDecBetweenTwoLSFs(String p_strln){ int intLen = p_strln.length (); retu rn p_strln.substring(intLen — 2, intLen — l).equals(”.” ); } / ** * This method will retrieve the two least significant digits from a number * as a number in the interval (0,99). * @param pjstrln Number to look at * ©param p_booDec True if the two LSFs have a decimal between them * @return Two least significant figures of the input as a number in (0,99) */ protected int getTwoLSFs(String p-strln, boolean p_booDec){ int intLen; //Length of the string containing the number int intLSFsNum; //Value being returned

    // Initialize intLen = p_strln. length ();

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 106

    //Return the two LSFs if (p.booDec == true){ return Integer.parselnt(p_strln .substring(intLen — 3, intLen — 2) + p -strln .substring(intL en — 1, intL en)); } e lse { return Integer.parselnt(p_strIn .substring(intLen — 2, intLen)); >

    /** * This method will retrieve information from a number by looking at * its two least significant digits modulo the interval base to get the * embedded information. * @param p_strln Number to look at */ protected void extractFromNum(String p_strln){ int intTwoLSFs; //Two least significant figures of the number

    / / Get the two least significant figures intTwoLSFs = getTwoLSFs(p-StrIn, hasDecBetweenTwoLSFs(p-strln));

    / /Extract the embedded data and check it processBits(NUM_BITS, intTwoLSFs % getIntervalSize()); } /** * Class for hiding information in numbers that reside in a string. * Primary functionality from this class implemented in the methods {@link * TweakTwoNumsMinErr#meetsReqs(String) TweakTwoNumsMinErr.meetsReqs} and * {©link TweakTwoNumsMinErr#embedInNum(String) * TweakTwoNumsMinErr.embedlnNum} so that subclasses of {@link * TweakTwoNumsMinErr TweakTwoNumsMinErr} do not have to also extend this * c la ss. */ protected class NumEncoder extends TweakNums.NumEncoder { /** * This method will scan a number, hiding information in it if it meets * the requirements of the encapsulating class ’ { ©link * TweakTwoNumsMinErr#meetsReqs(String) TweakTwoNumsMinErr.meetsReqs}. * @param p-strln Number to scan * @return String with number potentially modified */ protected String scanNum(String p_strln){ if (meetsReqs(p_strIn)){return tweakNum(pjstrln);} else {return p_strln;} } / ** * This method will hide information in a number using the * encapsulating class ’ {©link TweakTwoNumsMinErr#embedInNum(String) * TweakTwoNumsMinErr.embedlnNum}. This method will also perform error * calculations for analysis of the algorithm. * @param p_strln Number to adjust * ©return String with number modified */ protected String tweakNum(String p_strln){ String strResult; / /The result of tweaking double dblErr; /* Absolute value of the difference between original and tweaked divided by the original * /

    / /Tweak the number

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 107

    strResult = embedlnNum(p-strln);

    //Calculate the error dblErr = Math.abs((Double.parseDouble(strResult) — Double.parseDouble(p-strln))/Double. parseDouble(p_strIn)); if(m_dblMaxErr < dblErr){m_dblMaxErr = dblErr;}

    //Summing the error caused by tweaking squared to calculate root / /mean square error m_dblErrSum + = Math.pow(dblErr, 2);

    //Increment the tweaked numbers count &: return the tweaked number m JntTweakCount H—h; return strResult; } } /** * Class for retrieving information from numbers that reside in a string. * Primary functionality from this class implemented in the methods {@link * TweakTwoNumsMinErr/t meetsReqs (String) TweakTwoNumsMinErr.meetsReqs} and * {@link TweakTwoNumsMinErr#extractFromNum(String) * TweakTwoNumsMinErr.extractPromNum} so that subclasses of {©link * TweakTwoNumsMinErr TweakTwoNumsMinErr} do not have to also extend this * c la ss. */ protected class NumDecoder extends TweakNums.NumDecoder { /** * This method will scan a number, extracting information from it if it * meets the requirements of the encapsulating class ’ { @link * TweakTwoNumsMinErr#meet.sReqs(String) TweakTwoNumsMinErr.meetsReqs}. * @ par am p_strln Number to scan * ©return String with number potentially modified */ protected String scanNum(String p_strln){ if (meetsReqs(p-strln)){return tweakNum(p_strIn);} else {return p_strln;} } /** * This method will retrieve information from a number using the * encapsulating class ’ {@link * TweakTwoNumsMinErr#extractFromNum(String) * TweakTwoNumsMinErr.extractFromNum}. * ©param pjstrln Number to look at * ©return String with the number (just here because of superclass) */ protected String tweakNum(String p_strln){ / /Extract the embedded information from the number extractFromNum(p_strIn); //Just return the string with no processing return p_strln; } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 108

    Listing B.14. OrderEncoder. java import java.math.Biglnteger; import java.util.ArrayList; import java.util. Iterator; import java.util. Listlterator ; import java.util.TreeMap; /** * The OrderEncoder class for hiding information in the order of * items in a list (set of SVG tags, set of SVG attributes). *

    Completed 8/28/04 * @author Jerry Hungerman */ public class OrderEncoder { /** * Points to the information generator for the information to embed */ protected InfoGen mJnfoEmbed; /** * This method inserts the information generator to use. * @param pJnfoEmbed Information Generator to use */ public void insertInfoGen(InfoGen p_infoEmbed){ mJnfoEmbed = pJnfoEmbed; } /** * This method encodes information in the order of the items in the * TreeM ap. * @param p.TM Map of information to reorder * @return List of information ordered to hide information */ public ArrayList encodelnList(TreeMap p_TM){ ArrayList objOrderedList; //Info ordered to hide information Iterator objKeys; //Keys to all the info in the TreeMap Biglnteger objVal; //Information to encode Biglnteger objNewPos; //New position of an item in the list int i; //Counting variable

    / / Initialize objOrderedList = new ArrayList(p_TM.size()); objKeys = p.TM.keySet().iterator(); if (objKeys.hasNext()){ objOrderedList.add(0, p_TM.get(objKeys.next())); } if (objKeys.hasNext() = = false){ / /There is one or no items in the list return objOrderedList;

    //R e—order the list objVal = getNewInfo(p_TM.size()); i = 2; w hile (objKeys.hasNext()){ objNewPos = objVal.mod(BigInteger.valueOf(i)); ob j OrderedList. add (ob j N ewPos. int Value(), p_TM.get(objKeys.next())); objVal = (objVal.subtract(objNewPos)).divide( Biglnteger. valueOf(i)); i+ + ; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 109

    return objOrderedList; }

    /** * This method decodes information from the order of the items in the * ArrayList. * @param p_alKeys List of keys of the information to decode * @return Information decoded */ public Biglnteger decodeFromList (ArrayList p_alKeys){ Object [] objKeys; //Array for processing keys TreeMap tmOrdered; //Keys in their natural ordering Object [] objOrdKeys; //Array of keys in their natural ordering Biglnteger objVal; //Value being extracted int i; //Counting variable

    / / Initialize tmOrdered = new TreeMap(); objKeys = p_alKeys.toArray(); objVal = Biglnteger. valueOf(O);

    //Create a list of the keys in their natural order for (i = 0; i < objKeys.length; i++){ tm O rdered. pu t (ob j Keys [i], ob j Keys [i ]); } objOrdKeys = tmOrdered.keySet().toArray();

    //G o through the natural ordering backward for decoding for (i = objOrdKeys.length; i > 1; i ){ objVal=(objVal.add(BigInteger.valueOf( numPrevInBoth(objKeys, objOrdKeys[i — l])))).multiply( Biglnteger.valueOf(i — 1)); }

    //R eturn the information extracted return objVal; } /** * This method gets the number of items that fall before the current key in * both the natural ordering and the ordering of the list being decoded. * @param p_objKeys Keys in the order they fall in the list being decoded * (those that fall after the current key naturally have a * value of null in their position in this array) * @param p-objKey Current key * @return Number of items found that fall before the current key in both * natural ordering and the list being decoded */ protected int numPrevInBoth(Object|] p.objKeys, Object p_objKey){ int i; //Counting variable int count; //Number of items before the current key in both the //ordered list and the list to decode / / Initialize i = 0; count = 0;

    w hile (p-objKey.equals(p.objKeys[i]) = = false) { //If a preceding item is not yet null, then it is also comes before //the current key in the natural ordering if (p_objKeys[i] != null) count++;

    i+ + ; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 110

    //Set the current key to null and return the count p_objKeys[i] = null; return count;

    /** * This method will get randomly generated information to embed, based * on the size of the list being used. * @param p_numltems Number of items in the list * @return Information generated */ protected Biglnteger getNewInfo(int p_numltems){ Biglnteger objNewInfo; //New information

    objNewInfo = Biglnteger. valueOf(O); for (int i = getNumBits(p_numItems); i > 0; i ){ if (m_infoEmbed.next() = — 1){ objNewInfo = objNewInfo.setBit(i — 1); > > return objNewInfo; } /** * This method figures out the number of bits that can be encoded, * given the length of a list to re—order. * @param p_numltems Number of items in the list * ©return The number of bits that can be encoded */ public int getNumBits(int p_numltems){ Biglnteger objNumPossibilities; //For calculation (n! gets big)

    / / Calculate the number of possible list orderings objNumPossibilities = Biglnteger.valueOf(l); for (int i = 2; i < = p.num ltem s; i+ + ){ objNumPossibilities = objNumPossibilities.multiply( Biglnteger. valueOf(i)); } return objNumPossibilities.bitLength() — 1; } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. I l l

    Listing B.15. ReOrderElements . java im port java.io.File; im port java.math.Biglnteger; im port java.util.ArrayList; im port java.util. Listlterator ; /** * The ReOrderTags class for embedding information in SVG files * using the order of SVG tags in the source file . *

    Completed 8/28/04 * @author Jerry Hungerman */ public class ReOrderElements extends TagProcessor implem ents StegMethod { /** * SVG document to manipulate the ordering of tags */ protected OrderedSVGDoc m.objDoc = new OrderedSVGDoc(); /** * Object that hides information in the order of a list */ protected OrderEncoder m_OE = new OrderEncoder();

    /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = V;

    /** * This method gets the abbreviation of the steg method. * @return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; } /** * This method initializes the information generator. * @param pJngSeed Random number seed for gen */ protected void setupInfoGen(long pJngSeed) { mJnfoEmbed = new InfoGen(pJngSeed); m_OE.insertInfoGen(m JnfoEmbed); } /** * This method encodes information in the ordering of SVG tags. * @param p-fileSVG SVG file to embed information into * @param p.lngSeed Random number seed for sample data to embed * @return The number of bytes successfully encoded */ public float encode(File p_fileSVG, long pJngSeed){ SVGTag objTag; //An SVG tag Listlterator objReOrderedTags; //Tags reordered to encode information

    / / Initialization init (p-fileSVG, pJngSeed, ”_s” + getAbbrev() + ”.svg”);

    //Start processing objTag = m_objTextRW.readTag(); while (objTag != null){ //P ut the current tag in its ” natural” ordering in the document

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 112

    //structure processTagForEncode(objTag);

    //G et the next tag objTag = m.objTextRW.readTag(); }

    //Reorder tags to encode information objReOrderedTags = getReOrderedDoc().listIterator();

    / / Output the new ordering of tags while (objReOrderedTags.hasNext()) { //Call the method in LeadTab so that the tags are properly output su p e r. processTagForEncode ((S V GTag) ob j ReOrderedTags. next ()); }

    if (mJntlndent != 0){ System.out.println(” SVG-not-properly-formed.-The-output-fileJ’ + ” reflects -th is .” ); }

    / /Close the input and output files m.objTextRW.closeFiles();

    //Return the amount of information embedded return mJnfoEmbed.bytesProduced(); } /** * This method processes an SVG tag from the input file adds it to the SVG * document in memory for ordering. * @param p_objTag The SVG tag to process */ protected void processTagForEncode(SVGTag p_objTag){ / /Add the tag to the OrderedSVGDoc m_objDoc.addTag(p.objTag); } /** * This method encodes information by reordering tags from their natural * ordering (by the hash value of the string representing each tag). * ©return List of SVG tags in an order that encodes information */ protected ArrayList getReOrderedDoc(){ ArrayList objNewOrder; //List of tags in encoded order OrderedSVGTag objCurrTag; //Current tag being processed

    // Initialize objNewOrder = new ArrayList(m_objDoc.numTags());

    //Add the XML declaration objCurrTag = m.objDoc.getXMLDeclaration(); if (objCurrTag != null){objNewOrder.add(objCurrTag);}

    //A dd the any other tags at the document level objNewOrder. addAll(m_objDoc. get OtherTagsQ);

    / /Add the processing instructions objNewOrder. addAll( m-OE.encodelnList(m-objDoc.getProcInstructionsQ));

    //A dd the DOCTYPE declaration objCurrTag = m_objDoc.getDocTypeTag(); if (objCurrTag != nuil){objNewOrder.addAll(reOrderTags(objCurrTag));}

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 113

    //A dd the SVG document objCurrTag = m.objDoc.getSVGTag(); if (objCurrTag != null){objNewOrder.addAU(reOrderTags(objCurrTag));}

    return objNewOrder; } /** * This method encodes information by reordering a tags children from their * natural ordering (by the hash value of the string representing each tag). * @param p_obj Parent Tag The SVG parent tag to process * ©return List of SVG tags in an order that encodes information *i protected ArrayList reOrderTags(OrderedSVGTag p_objParentTag){ ArrayList objNewOrder; //List of tags in encoded order Listlterator objChildren; //Children of the tag

    / / Initialize objNewOrder = new ArrayList(p_objParentTag.numChildren() + 2);

    //A dd the tag itself objNewOrder.add(p_objParentTag);

    / /Add any children that cannot be reordered objChildren = p_objParentTag.getStaticChildren(). listlterator (); while (objChildren.hasNext()){ obj N ewOrder. add All (reOrderTags ( (OrderedS VGTag) obj Children. next ())); }

    if (p-objParentTag.hasChildrenToOrder()){ //Reorder the children objChildren = m_OE.encodeInList( p J3bjParentTag.getChildrenToOrder()). listlterator ();

    //A dd each child while (objChildren.hasNext()){ objNewOrder.addAll(reOrderTags( (OrderedSVGTag)objChildren.next())); } }

    //A dd the end tag if (p-objParentTag.hasEnd()){objNewOrder.add(p_objParentTag.getEnd());}

    objNewOrder.trimToSize(); return objNewOrder; } /** * This method gathers the SVG tags from the input file and to be analyzed * for hidden information. * @param p-objTag The SVG tag to process */ protected void processTagForDecode(SVGTag p-objTag) { //A dd the tag to the OrderedSVGDoc m-objDoc.addTag(p-objTag); } /** * This method decodes information from the SVG document by looking at the * ordering of tags. */

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 114

    protected void finalDecodeProcessing(){ OrderedSVGTag objCurrTag; //Current tag being processed

    //Decode the order of the processing instructions decodeOrderOfTags(m-objDoc.getProcInsKeysAsReadlnQ);

    //Decode the DOCTYPE declaration objCurrTag = m_objDoc.getDocTypeTag(); if (objCurrTag != null){decodeChildren(objCurrTag);}

    //Decode the rest of the SVG document objCurrTag = m-objDoc.getSVGTag(); if (objCurrTag != null){decodeChildren(objCurrTag);}

    /** * This method decodes information from the ordering of a tag’s children. * ©par am p_obj Parent Tag Tag whose children are to be scanned */ protected void decodeChildren(OrderedSVGTag p.objParentTag){ Listlterator objKeys; //Keys of the children ordered as read in

    //If the tag has children to order then it has children to decode if (p-ob j Parent Tag. hasChildrenToOrder ()) { / /Decode the order of the children decodeOrderOfTags (p-obj ParentTag.getChildKeys AsReadIn());

    / /Process each child under the parent objKeys = p_objParentTag.getChildKeysAsReadIn().listIterator(); while (objKeys.hasNext()){ decodeChildren((OrderedSVGTag) p_objParentTag.getChildrenToOrder().get (objKeys. next ())); } } } /** * This method decodes information from the SVG document by looking at the * ordering of tags. * ©param p_alTagKeys List of SVG tag keys in the order they were read in */ protected void decodeOrderOfTags(ArrayList p_alTagKeys){ Biglnteger objVal; //Information hidden in the tag order

    / /Decode information based on the order of the keys objVal = m_OE.decodeFromList (p_alTagKeys);

    / /Ensure the information is correct for (int i = m.OE.getNumBits(p.alTagKeys.size()); i > 0; i ){ if (objVal.testBit(i — 1)) {processBit(l);} else (processBit(O);} } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 115

    Listing B. 16. OrderedSVGDoc. j ava im port java.util.ArrayList; im port java.util.Stack; im port java.util.TreeMap;

    /** * The OrderedSVGDoc class for handling the ordering of tags in an * SVG document. *

    Completed 8/28/04 * ©author Jerry Hungerman */ public class OrderedSVGDoc { /** * The XML declaration */ protected OrderedSVGTag m_objXMLDecl = null;

    /** * The keys of the processing instruction tags in the order they were read * in from the input file */ protected ArrayList m_alProcInsKeys = new ArrayList();

    /** * Any processing instructions in the document */ protected TreeMap m_tmOrderedProcIns = new TreeMap();

    /** * The DOCTYPE declaration tag */ protected OrderedSVGTag m-objDocTypeTag = null;

    /** * The SVG document element tag */ protected SVGDocElem m.objSVGTag = null;

    /** * Any other tags at the document level */ protected ArrayList m_alOtherTags = new ArrayListQ;

    * The number of tags in the document */ protected int intNumTags = 0;

    /** * Stack for keeping track of SVG tags */ protected Stack m_objTags = new Stack(); I ** * Pointer to the current tag being processed in the document */ protected OrderedSVGTag m.objCurrTag = null;

    /** * This method adds a tag to a TreeMap of other tags for ordering, either * under a parent (e.g ., the SVG tag) or in the header of the file (e.g., * processing instructions ).

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. * @param p_objTag SVG tag to add to the TreeMap for ordering * @param p.TM TreeMap to add the tag to */ public static void addTagForOrdering(OrderedSVGTag p-objTag, TreeMap p_TM){ OrderedSVGTag objTagSameKey; //Tag with same key as new tag to put

    / / Add the tag for ordering objTagSameKey = (OrderedSVGTag) p_TM.put(p_objTag.key(), p_objTag);

    //If there is a tag with the same key, manipulate it to get a / / different key while (objTagSameKey != null){ //A dd a space at the end of the tag — a hack to get a different key objTagSameKey.addSpace(); objTagSameKey = (OrderedSVGTag) p_TM.put(objTagSameKey.key(), objTagSameKey); } } /** * This method gets the number of tags currently in the document object. * @return Number of tags currently in the document */ public int numTags(){ return intNumTags; } /** * This method gets the XML declaration. * ©return The XML declaration */ public OrderedSVGTag getXMLDeclaration(){ return m.objXMLDecl; } /** * This method gets the XML processing instructions. * @return The processing instructions ordered naturally */ public TreeMap getProcInstructions(){ return m.tmOrderedProcIns; } /** * This method gets the DOCTYPE tag. * ©return The DOCTYPE tag */ public OrderedSVGTag getDocTypeTag(){ return m_objDocTypeTag; } /** * This method gets the SVG document element. * ©return The SVG document element */ public SVGDocElem getSVGTag(){ return m_objSVGTag; } /** * This method gets the other tags at the document level. * @return The other tags at the document level */ public ArrayList getOtherTags(){

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 117

    return m.alOtherTags; } /** * This method will get the keys of the processing instructions in the * order that the tags were read in from the input file . * ©return The keys of the processing instructions as read in */ public ArrayList getProcInsKeysAsReadIn(){ return m_alProcInsKeys; }

    /** * This method adds a tag to the document. * @param p-objTag SVG tag to add to the document */ public void addTag(SVGTag p_objTag){ OrderedSVGTag objNewTag; / /Tag to add to the document String strName; //Name of the tag

    //P u t the tag in the ordering framework objNewTag = new OrderedSVGTag(p_objTag); strName = objNewTag.getName(); intNumTags++;

    / /Process where the new tag should go in the document if (objNewTag.isEnd() = = false){ //Push the current start tag on the stack m_objTags.push(objNewTag.getName());

    / / Check for special tags if (strName.equals(” !DOCTYPE”)){m.objDocTypeTag = objNewTag;} if (strName.equals(”svg”)){ if (m_objSVGTag == null){ m_objSVGTag = new SVGDocElem(objNewTag); m_objCurrTag = m_objSVGTag; } else { //A dd the nested SVG element SVGDocElem objNestedSVGElem = new SVGDocElem(objNewTag); m_objCurrTag = m_objCurrTag.addChild(objNestedSVGElem); } } else { //A dd the tag as a child to the current tag, moving the current //tag to the child if (m-objCurrTag != null){ m_objCurrTag = m_objCurrTag.addChild(objNewTag); } else { m-objCurrTag = objNewTag; } } } else{ //See if the current end tag matches the current start tag if (m-objTags.empty()==false){ if (objNewTag.getName().equals(”/ ” 4- m_objTags.peek())){ m.objTags.pop(); m_objCurrTag.set End (objNewTag); m_objCurrTag = m_objCurrTag.getParent(); } else { / / Add the tag as a child to the current tag, keeping the

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 118

    //current tag intact m_objCurrTag.addChild(objNewTag); } } e lse { //Tag is not under the document element, so it must be //something in the header nuobjCurrTag = null;

    if (strName.startsWith( if (strName.equals(”?xml”)){ //XM L declaration m-objXMLDecl = objNewTag; } e lse { //Processing instruction addTagForOrdering(objNewTag, m.tmOrderedProcIns); m_alProcInsKeys.add(objNewTag.key()); } } e lse { if (strName.equals(”!DOCTYPE”)){ //The DOCTYPE tag m.objDocTypeTag = objNewTag; } e lse { if (strName.equals(”svg”)){ / /The SVG document element m-objSVGTag = new SVGDocElem(objNewTag); } e ls e { //Another tag outside the SVG document element m-alOtherTags.add(objNewTag); } } } } } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 119

    Listing B.17. OrderedSVGTag. java im port java.util.ArrayList; im port java.util.TreeMap;

    /** * The OrderedSVGTag class for handling the ordering of SVG tags. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class OrderedSVGTag extends SVGTag { /** * Title tag that is a child of the tag (FUTURE ADDITION) */ protected OrderedSVGTag m_objTitle = null;

    /** * Any children of the tag that cannot be re—ordered */ protected ArrayList m_alOtherTags = new Array List ();

    /** * The matching end tag (if one exists) */ protected OrderedSVGTag m-objEndTag = null;

    /** * The parent of the tag (null if the document element) */ protected OrderedSVGTag m_objParent = null;

    /** * The keys of the children of the tag as they were read in from the input * file */ protected ArrayList m_alChildKeys = new ArrayList();

    * The children of the tag in natural order */ protected TreeMap m_tmOrderedChildren = new TreeMap();

    /** * The number of children on the tag */ protected int mJntNumChildren = 0;

    * A constructor for the class. * @param p_objTag SVG tag to be ordered */ public OrderedSVGTag(SVGTag p.objTag){ super(p.objTag.getFullTag(), p.objTag.getLeadingTxtQ); } /** * A constructor for the class. * @param p.objTag SVG tag to be ordered */ public OrderedSVGTag(OrderedSVGTag p_objTag){ super(p.objTag.getFullTagQ, p_objTag.getLeadingTxt()); }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 120

    /** * This method returns the key for the SVG tag for sorting and ordering * purposes. * ©return The key for the SVG tag */ public Integer key(){ return new Integer(this.getFullTag().hashCode()); } /** * This method adds the matching end tag (if one exists). * @param p_objTag SVG tag to be added as the end tag */ public void setEnd(OrderedSVGTag p.objTag){ / /Add the child m.objEndTag = p_objTag; } /** * This method will check if the tag has a matching end tag. * ©return True if the tag has a matching end tag */ public boolean hasEnd(){ return (m_objEndTag != null); }

    /** * This method gets the matching end tag. * ©return SVG tag that is the end tag */ public OrderedSVGTag getEnd(){ return m.objEndTag; } /** * This method adds a child tag. * (Sparam p_objTag SVG tag to be added as a child * ©return The child tag, now linked with its parent *1 public OrderedSVGTag addChild(OrderedSVGTag p_objTag){ / / Add the child P-objTag.setParent(this); m JntN umChildren++; if (tagForOrdering(p_objTag)){ OrderedSVGDoc.addTagForOrdering(p.objTag, m_tmOrderedChildren); m_alChildKeys. add (p-ob j Tag. key ()); } //Return the child just processed return p_objTag; > /** * This method will check if a child tag is to be ordered. * @param p_objTag SVG child tag to be checked * @return True if the child tag is to be ordered under the tag */ protected boolean tagForOrdering(OrderedSVGTag p_objTag){ //FUTURE ADDITION: keep title and comment tags in a meaningful order to //make the source file look a little more standard

    //Cannot re—order tspan tags if (p_objTag.getName() .equals (” tspan”)) { m_alOtherTags.add(p_objTag); return false;

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 121

    }

    //A ll other tags can be re—ordered return true; } /** * This method will check if the tag has children. * @ return TYue if th e ta g has children */ public boolean hasChildren(){ return !((m.objTitle==null) ScSc (m_alOtherTags.isEmpty()) && (m_tmOrderedChildren.size()==0)); } /** * This method get the number of children on the tag. * @return The number of children on the tag */ public int numChildren(){ return mJntNumChildren; } /** * This method will check if the tag has children to order. * @return True if the tag has children to order */ public boolean hasChildrenToOrder(){ return (m_tmOrderedChildren.size() > 0); }

    /** * This method will get the children of the tag that can’t be reordered. * C<('return The children of the tag that can’t be reordered */ public ArrayList getStaticChildren(){ ArrayList objStaticChildren; //Children that can’t be reordered

    objStaticChildren = new ArrayList(); //A dd the title if (m-objTitle != null){objStaticChildren.add(m_objTitle);} //A dd any comments objStaticChildren. addAll(m_alOtherTags);

    return objStaticChildren; } /** * This method will get the keys of the children of the tag in the order * that the tags were read in from the input file . * ©return The keys of the children of the tag as read in */ public ArrayList getChildKeysAsReadIn(){ return m.alChildKeys; }

    f ** * This method will get the children of the tag that can be reordered. * ©return The children of the tag that can be reordered */ public TreeMap getChildrenToOrder(){ return m.tmOrderedChildren; }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 122

    /** * This method sets the parent of the tag. * @param p_objTag SVG tag to be referenced as the parent */ public void setParent(OrderedSVGTag p_objTag){ m.objParent — p_objTag; } I ** * This method gets the parent of the tag. * @return SVG tag that is the parent of the tag */ public OrderedSVGTag getParent(){ return m.objParent; } /** * This method adds a space to the end of the tag, before the ending brace. */ public void addSpace(){ //Make sure any changes to the tag are captured t his. getFullTag ();

    if (m_strData.endsWith(” /> ”)){ m-strData = m_strData.substring(0, m-StrData.length() — 2).concat(”~/>”); } e lse { if (m_strData.endsWith(”?>”)){ m_strData = m_strData.substring(0, m_strData.length() — 2) .concat (”_?>”); } e lse { if (m_strData.endsWith(” >”)){ m-strData = m_strData.substring(0, m_strData.length() — 3).concat(”« >”); } e lse { m_strData = m_strData.substring(0, m_strData.length() — l).concat (”„>”); } } } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 123

    Listing B. 18. SVGDocElem. j ava import java.util.TreeMap;

    / ** * The SVGDocElem class for the SVG document element. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class SVGDocElem extends OrderedSVGTag { /** * Any defs tags under the SVG document element (FUTURE ADDITION) */ protected TreeMap m.tmDefsTags = new TreeM ap(); / ** * Any script tags under the SVG document element (FUTURE ADDITION) */ protected TreeMap m-tmScriptTags = new TreeMap();

    /** * A constructor for the class. * @param p.objTag SVG tag that is the document element */ public SVGDocElem(OrderedSVGTag p.objTag){ super (p.objTag); } /** * This method will check if a child tag is to be ordered. * @param p.objTag SVG child tag to be checked * @return True if the child tag is to be ordered under the tag */ protected boolean tagForOrdering(OrderedSVGTag p.objTag){ //N o check for tspan elements: they are always under a text element

    //FUTURE ADDITION: set aside defs and script elements to make the //source file look a little more standard return true; } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 124

    Listing B. 19. R e O rd e rA tts.ja v a im port java.math.Biglnteger; im port java.util.Vector; im port java.util.ArrayList; im port java.util.TreeMap; /** * The ReOrderAtts class for embedding information in SVG files * using the order of SVG attributes in each tag in the source file . *

    Completed 8/28/04 * @author Jerry Hungerman */ public class ReOrderAtts extends TagProcessor implem ents StegMethod { I** * Object that hides information in the order of a list */ protected OrderEncoder m.OE = new OrderEncoder();

    /** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = ’u’;

    /** * This method gets the abbreviation of the steg method. * @return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; >

    /** * This method initializes the information generator. * @param pJngSeed Random number seed for gen */ protected void setupInfoGen(long p-lngSeed){ mJnfoEmbed = new InfoGen(p_lngSeed); m_OE.insertInfoGen(m JnfoEmbed); } /** * This method manipulates an SVG tag to encode information in the ordering * of the attributes on the tag. * @param p-objTag SVG tag to manipulate * @return The new SVG tag */ protected SVGTag manipTag(SVGTag p_objTag){ Vector vAtts; //Any attributes on the tag TreeMap tmAtts; //The attributes sorted naturally SVGAttribute objAtt; //A n attribute on the tag

    / / Cannot adjust the attributes on the XML declaration if (p-objTag.getName().equals(”?xml”)) return p-objTag;

    //Encode information by re—ordering attributes on the tag vAtts = p_objTag.getAtts(); if (vAtts != null){ if ( vA tts. size () > 1) { //Order the attributes naturally tmAtts = new TreeMap(); for (int i = 0; i < vAtts.size (); i++){ //G et the attribute and add it to the TreeMap

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 125

    objAtt = (SVGAttribute) vAtts.elementAt(i); objAtt = (SVGAttribute) tmAtts.put(objAtt.key(), objAtt);

    //There should never be another attribute with the same key if (objAtt != null){ System.out.printlnf” Two_attributes_with_the_same_nameJ’ + ”found_on_tag:_” + p_objTag.getPullTag()); } }

    / /R e—order attributes to hide information vAtts. clear (); vAtts.addAll(m_OE.encodeInList(tmAtts)); } }

    //Return the new tag return p_objTag; ' } /** * This method processes an SVG tag from the input file and analyzes it for * hidden information in the order of the attributes on the tag. * @param p.objTag The SVG tag to process */ protected void processTagForDecode(SVGTag p_objTag){ Vector vAtts; //Any attributes on the tag ArrayList alKeys; //List of keys of the attributes on the tag SVGAttribute objAtt; //A n attribute on the tag Biglnteger objVal; //Information hidden in the tag

    //Cannot adjust the attributes on the XML declaration if (!(p-objTag.getName().equals(”?xml”))){

    //G et the attributes vAtts = p_objTag.getAtts(); if (v A tts != null){ if (v A tts.size () > 1){ alK eys = new ArrayList(vAtts.size()); for (int i = 0; i < vAtts.size (); i++){ //Add the key of the attribute to the list for decode objAtt = (SVGAttribute) vAtts.elementAt(i); alKeys.add(objAtt.key()); }

    //Decode information based on the order of the keys objVal = m_OE.decodeFromList(alKeys);

    / /Ensure the information is correct for (int i = m_OE.getNumBits(vAtts.size()); i > 0; i ){ if (objVal.testBit (i — 1)) {processBit(l);} else {processBit(O);} } } } } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. Listing B.20. AllMethods.java im port java.io.File; im port java.io.FileWriter; im port java.io.FileReader; im port java.io.BufferedWriter; im port java.io.BufferedReader; im port java.io.IOException;

    /** * The AllMethods class for embedding information in SVG files * using ReOrderAtts, TweakTwoNums, and * LeadTabSqOff. *

    Completed 8/28/04 * @author Jerry Hungerman */ public class AllMethods extends StegAlgorithm implem ents StegMethod { f ** * Abbreviation for specifying use of the algorithm in SVGSteg */ public static final char ABBREV = ’a’; j ** * This method gets the abbreviation of the steg method. * @return The abbreviation of the steg method */ protected char getAbbrev(){ return ABBREV; } /** * This method encodes information using a combination of the methods * developed. * @param pJileSVG SVG file to embed information into * @param pJngSeed Random number seed for sample data to embed * @return The number of bytes successfully encoded */ public float encode(File p_fileSVG, long pJngSeed){ File fileln ; //SVG file for input File fileOut; //SVG file for output StegMethod objMethod; //Class for embedding/extracting information float bytesEmbedded; //Total number of bytes embedded

    // Initialization fileln = initTempFile(p-fileSVG); fileOut = initOutput(p_fileSVG);

    //R un all of the methods //Reorder of attributes objMethod = new ReOrderAtts(); bytesEmbedded = objMethod.encode(fileIn, pJngSeed);

    //Tweak two least significant figures fileln = initTempInput(fileln, ”_s” + ReOrderAtts.ABBREV + ”.svg”); objMethod = new TweakTwoNums(); bytesEmbedded - ■ bytesEmbedded + objMethod.encode(fileIn, p-lngSeed);

    //Leading Tabs, squaring off to 80 character lines fileln = initTempInput (fileln, ”_s” + TweakTwoNums.ABBREV + ”.svg”); objMethod = new LeadTabSqOff(); bytesEmbedded = bytesEmbedded + objMethod.encode(fileIn, pJngSeed);

    //Rename the last output file to reflect the AllMethods run

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 127

    fileln = initFile( fileln , ”_s” + LeadTabSqOff.ABBREV + ”.svg”); if ( fileln .renameTo(fileOut) = = false) { System.out.println(” Unable-to~rename~output-file.” ); }

    //Return the amount of information embedded return bytesEmbedded; }

    I** * This method decodes information using a combination of the methods * developed. * ©param p_fileSVG SVG file to extract information from * ©param pJngSeed Random number seed for sample data to embed * ©return True if the embedded information matches what is * generated with the seed passed in */ public boolean decode(File p_fileSVG, long pJngSeed) { StegMethod objMethod; //Class for embedding/extracting information

    // Initialize m.booMatches = true;

    //R un all of the methods //Reorder of attributes objMethod = new ReOrderAtts(); System.out.println (); System.out.print (” Reorder-AttributesJ’); if (objMethod.decode(p_fileSVG, p-lngSeed) = = false) { System.out.println(”„_JteOrderAttsodecode-failed!!!”); m.booMatches = false; }

    //Tweak two least significant figures objMethod = new TweakTwoNumsQ; System.out.println (); System.out .print (”. ___ T weak.T wo-N umbers J ’); if (objMethod.decode(p_fileSVG, pJngSeed) = = false){ System.out.println(” _ _ T weakT woNums_decode.failed!!!”); m.booMatches = false; }

    //Leading Tabs, squaring off to 80 character lines objMethod = new LeadTabSqOff(); System.out.println (); System.out.print(” Lead_Tab~Square~OflL”); if (objMethod.decode(p_fileSVG, pJngSeed) = = false){ System, out. println(” ___ I.eadTabSqOfLxlecode.failed!!!”); m-booMatches = false; }

    System.out.println (); return m.booMatches; }

    /** * This method initializes a temporary input file for one of the methods in * the process, based upon the output from the previous method. * ©param p_fileln Input file to the previous steg method * ©param p_strExt Extension added by the previous steg method * (©return File that is the output of the prvious steg method */ protected File initTempInput (File p-fileln, String p_strExt){ File fileTemp; //Temp file to create

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 128

    fileTemp = initFile( p-fileln , p_strExt);

    try { / /Make sure the file exists if (fileTemp.exists() == false){ throw new Exception(”Cannot_find-input„filej’ + + fileTemp.getName() + }

    //Make sure the file is deleted on exit fileTemp. deleteOnExitQ; > catch (Exception e){ System.out .printIn (e ); }

    //Return the abstract path name of the file return fileTemp; }

    /** * This method initializes the output file for the AUMethods * m ethod. * ©param p_fileSVG The SVG file input * ©return File that will be the output of AUMethods */ protected File initOutput(File p_fileSVG){ File fileOut; //SVG file for output

    fileOut = initFile (pJileSVG, ”_s” + getAbbrev() + ”.svg”);

    try { //Delete a previous version if necessary if (fileOut. exists ()){ if (fileOut. delete()==false){ throw new Exception(” Errorjcreating^output-SVG-fileJ’ + + fileOut.getName() + } } } catch (Exception e){ System.out. println(e); }

    //Return the abstract path name of the file return fileOut; } /** * This method initializes a file object based upon the file and extension * given. * @param p_fileln File to base the new name on * ©param pjstrExt Extension for the new file * ©return New file = [old file name without extension] + [new extension] */ protected File initFile (File p-fileln , String p_strExt){ String strName; //Name of the file String strPath; //Path of the file

    / / G et th e file nam e strName = getFileNameWithoutExt(pJileln) + p-strExt;

    / / G et th e file p a th

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 129

    strPath = pjileln .getParentQ; if (strPath != null){ strName = strPath + File.separatorChar + strName; }

    / /Return the abstract path name of the file return new File(strName); } / ** * This method gets the name of a file without its extension. * © param p_fileln Input file * @return Name of the input file without its extension */ protected String getFileNameWithoutExt(File p_fileln){ String strNewName; //File name to return

    strNewName = p_fileIn.getName(); retu rn strNewName.substring(0, strNewName.length() — 4); }

    /** * This method initializes a temporary file that is a copy of the input * file . * @param p-fileToCopy Input file to copy * ©return Temporary file that is a copy of the input file */ protected File initTempFile(File p_fileToCopy){ File fileTemp; //Temp file to create

    t r y { / /C rea te th e tem p file fileTemp = File.createTempFilef’tmp”, ”.svg”, (new File(p_fileToCopy.getAbsolutePath())).getParentFile());

    / /Copy the input file into the new temp file if (copyContentsToFile(p_fileToCopy, fileTemp) = = false){ System.out. println(” Unable„to _create_temporary„copy,jofJ’ + pJileToCopy.getName()); }

    //M ake sure the temp will be deleted on exit and then return it fileTemp. delet eOnExit (); return fileTemp; } catch (Exception e){ System.out.println(e); return null; } }

    /** * This method copies the contents of one file into another. * @param p-fileSource Source file * ©param p_fileDest Destination file * @return True if the copy is successful */ protected boolean copyContentsToFile(File p_fileSource, File p_fileDest){ BufferedReader brln; //Reads from source BufferedWriter bwOut; //Writes to destination String objText; //Text to move

    try { // Initialize

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 130

    brln = new BufferedReader(new FileReader(p_fileSource)); bwOut = new BufferedWriter(new FileWriter(pJileDest)); objText = brIn.readLine();

    / / Copy the contents of the source to the destination while (objText != null){ bwOut. write(objText); bwOut.newLine(); objText = brIn.readLine(); }

    / / Close the reader and writer if (brln != null) {brln . close ();} if (bwOut != null) {bwOut.close();} return true; } catch (Exception e){ S y stem .o u t.p rin tln (e); return false; } } }

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 131

    REFERENCES

    [1] Agrawal, R. and J. Kiernan. Watermarking relational databases. In Proceedings of the 28th International Conference on Very Large Databases VLDB, 2002.

    [2] Anderson, M. M. The electronic check architecture, 1998. Available on the Internet at http://www.echeck.org/library/wp/ArchitectualOverview.pdf (accessed 23 October, 2006).

    [3] Backes, M. and C. Cachin. Public-key steganography with active attacks. Technical Report 2003/231, IBM Research, Zurich Research Laboratory, 2003.

    [4] Batik SVG toolkit. The Apache Software Foundation. Available on the Internet at http:/ / xml.apache.org/batik/ (accessed 23 October, 2006).

    [5] Battiato, S., G. Di Blasi, G. Gallo, and E. Messina. Firemark: a Java tool for SVG watermarking. In Proceedings of the 4th Annual Conference on Scalable Vector Graphics, 2005.

    [6] Bender, W., D. Gruhl, and N. Morimoto. Techniques for data hiding. IBM Systems Journal, 35(3/4):131-336, 1996.

    [7] Bloom, J. and R. Alonso. SmartSearch steganalysis. In Proceedings of SPIE, Security and Watermarking of Multimedia Contents V, 2003.

    [8] Brown, A. GZSteg steganography tool. Available on the Internet at http://linkbeat.com/ files/ (accessed 23 October, 2006).

    [9] Brown, A. S-Tools steganography tool. Available on the Internet at ftp://ftp.demon.net/ pub/mirrors/crypto/idea/code/s-tools4.zip (accessed 23 October, 2006).

    [10] Cagle, K. SVG Programming: The Graphical Web. Apress, Berkeley, 2002.

    [11] Chandramouli, R. A mathematical framework for active steganalysis. Multimedia Systems Special Issue on Multimedia Watermarking, 9:303-311, Sep 2003.

    [12] Cole, E. Hiding in Plain Sight: Steganography and the Art of Covert Communication. Wiley Publishing, Indianapolis, 2003.

    [13] Cox, I. J., M. L. Miller, and J. A. Bloom. Digital Watermarking. Morgan Kaufmann, San Francisco, CA, 2002.

    [14] Craver, S., A. Stubblefield, and E. Felten. Reading between the lines: Lessons from the SDMI Challenge. In Proceedings of the 10th USENIX Security Symposium, 2001.

    [15] Davern, P. and M. Scott. Fractal based image steganography. In Proceedings of the First International Workshop on Information Hiding, 1996.

    [16] Davida, G. and M. T. Chapman. NiceText steganography tool. Available on the Internet at http://www.mirrors.wiretapped.net/security/steganography/nicetext (accessed 23 October, 2006).

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 132

    [17] Farid, H. Detecting steganographic messages in digital images. Technical Report TR2001- 412, D artmouth College, Hanover, NH, 2001.

    [18] Fisk, G., M. Fisk, C. Papadopoulos, and J. Neil. Eliminating steganography in Internet traffic with active wardens, 2002. Los Alamos National Laboratory.

    [19] Franz, E. Steganography preserving statistical properties. In Proceedings of the Fifth International Workshop on Information Hiding, 2003. [20] Fridrich, J., M. Goljan, and R. Du. Reliable detection of LSB steganography in color and grayscale images. In Proceedings of the ACM Special Session on Multimedia Security and Watermarking, 2001.

    [21] Fridrich, J., M. Goljan, and R. Du. Steganalysis based on JPEG compatibility. In Special session on Theoretical and Practical Issues in Digital Watermarking and Data Hiding, SPIE Multimedia Systems and Applications IV, 2001.

    [22] Fridrich, J., M. Goljan, and D. Hogea. Attacking the OutGuess. In Proceedings of the ACM Workshop on Multimedia and Security, 2002.

    [23] Gross-Amblard, D. Query-preserving watermarking of relational databases and XML documents. In ACM Symposium on Principles of Database Systems (PODS), 2003.

    [24] Herodotus. The Histories. J.M. Dent & Sons, London, England, 1992.

    [25] Hetzl, S. StegHide steganography tool. Available on the Internet at http://steghide .sourceforge.net/ (accessed 23 October, 2006). [26] The OpenNet Initiative. Internet filtering in China in 2004-2005: A country study, Apr 2005. Available on the Internet at http: / / www.opennetinitiative.net/studies/china/ (accessed 23 October, 2006). [27] Java API for XML processing (JAXP). Sun Microsystems, Inc. Available on the Internet at http://java.sun.com/xml/jaxp/ (accessed 23 October, 2006).

    [28] Johnson, N. F. Steganography and digital watermarking tool table. Available on the Internet at http://www.jjtc.com/Steganography/toolmatrix.htm (accessed 23 October, 2006).

    [29] Katzenbeisser, S. C. and F. Petitcolas. Information Hiding Techniques for Steganography and Digital Watermarking. Artech House, Norwood, MA, 2000. [30] Kawaguchi, E. and R. O. Eason. Principle and applications of BPCS steganography. In Proceedings of SP IE ’s International Symposium on Voice, Video, and Data Communications, Nov 1998. [31] Korejwa, J. JSteg steganography tool. Available on the Internet at http://zooid.org/ "paul/crypto/jsteg/ (accessed 23 October, 2006). [32] Kwan, M. The Gifshuffle home page. Available on the Internet at http://www.darkside .com.au/gifshuffle/ (accessed 23 October, 2006). [33] Kwan, M. Snow steganography tool. Available on the Internet at http: / / www.darkside .com.au/snow/index.html (accessed 23 October, 2006). [34] Latham, A. JPHide & Seek steganography tool. Available on the Internet at http:// linvrx01.gwdg.de/%7Ealatham/stego.html (accessed 23 October, 2006).

    [35] Lyu, S. and H. Farid. Detecting hidden messages using higher-order statistics and support vector machines. In Proceedings of the Fifth International Workshop on Information Hiding, 2003.

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission. 133

    [36] Mapquest. Available on the Internet at http://www.mapquest.com/ (accessed 23 October, 2006).

    [37] Mercuri, R. T. and P. G. Neumann. Inside risks: Security by obscurity. Communications of the ACM, 46(11):160, 2003.

    [38] Neumann, A. and A. Winter. Time for SVG—towards high quality interactive Web-maps. Available on the Internet at http://www.carto.net (accessed 23 October, 2006).

    [39] Petitcolas, F. mp3stego steganography tool. Available on the Internet at http://www.cl .cam.ac.uk/~fapp2/steganography/mp3stego (accessed 23 October, 2006).

    [40] Provos, N. OutGuess steganography tool. Available on the Internet at http://www .outguess.org/ (accessed 23 October, 2006).

    [41] Provos, N. Defending against statistical steganalysis. In Proceedings of the 10th USENIX Security Symposium, Aug 2001.

    [42] Provos, N. and P. Honeyman. Detecting steganographic content on the Internet. Technical Report 01-11, University of Michigan, 2001.

    [43] Scalable vector graphics (SVG) 1.1 specification. World Wide Web Consortium. Available on the Internet at http://www.w3c.org/TR/SVGll/ (accessed 23 October, 2006).

    [44] Mobile SVG profiles: SVG Tiny and SVG Basic. World Wide Web Consortium. Available on the Internet at http://www.w3c.org/TR/SVGMobile/ (accessed 23 October, 2006).

    [45] The Secure Digital Music Initiative (SDMI). Available on the Internet at http://www .sdmi.org/ (accessed 23 October, 2006).

    [46] Sion, R., M. Atallah, and S. Prabhakar. On watermarking semi-structures. Technical Report TR2001-54, Purdue University, Nov 2001.

    [47] Sion, R., M. Atallah, and S. Prabhakar. On watermarking numeric sets. In Proceedings of IWDW, 2002.

    [48] von Ahn, L. and N. J. Hopper. Public-key steganography, 2003. Submitted to Crypto.

    [49] Watt, A., C. Lilley, D. J. Ayers, R. George, C. Wenz, T. Hauser, K. Lindsey, and N. Gustavsson. SVG Unleashed. Sams Publishing, Indianapolis, 2003.

    [50] Wayner, P. Mimic Functions— The Manual. Available on the Internet at http://www.nic .funet.fi/pub/crypt/old/mimic/Mimic-Manual.txt (accessed 23 October, 2006).

    [51] Wayner, P. Disappearing Cryptography, Information Hiding: Steganography and Digital Watermarking. Morgan Kaufmann Publishers, San Francisco, second edition, 2002.

    [52] Westfeld, A. and A. Pfitzmann. Attacks on steganographic systems. In Proceedings of Information Hiding - Third International Workshop, 2000. [53] WetStone Stego Watch. Available on the Internet at http://www.wetstonetech.com/ stegowatch.html (accessed 23 October, 2006).

    [54] Zittrain, J. and B. Edelman. Documentation of Internet filtering worldwide. Available on the Internet at http://cyber.law.harvard.edu/filtering/ (accessed 23 October, 2006).

    Reproduced with permission of the copyright owner. Further reproduction prohibited without permission.