Hugo static blog engine with Mastodon comments: step by step quick howto
@ Tony Cheneau | Saturday, Jan 30, 2021 | 12 minutes read | Update at Saturday, Jan 30, 2021

In this short blog entry, I’ll explain how to add a comment system to Hugo’s static blog engine. I’ll expand on Carl Schwan’s excellent blog article and attempt to make it more accessible to Hugo’s newcomers. For the sake of example, I expand from the Dream theme (kudos to g1eny0ung for his great theme!).

Foreword

All the hard work has been pulled of by Carl. This is merely the steps I had to reproduce to integrate Carl’s suggestion. I hope it will help others like me, willing to bring some life into their blogs. Carl provides the source of his blog on Github, this is a valuable source of information.

Breakdown of the tutorial

This quick tutorial is divided in three steps:

  • modify your existing template to make it possible to add comments
  • adding Carl’s comment system
  • copying some Javascripp dependencies

However, I’ll start by showing you how easy it will be to add comments to an existing blog entry!

Finally: adding comments on an article!

Perhaps the best thing to start with is the thing you’ll do at the end: add comments with your new comment system. So it is important to take a moment and check that it indeed works as you want it to.

Hugo articles usually start with a YAML block. Here, you can just add the following block to add comments in your article (provided that you do everything else from this article):

---
# add this somewhere along with you parameters
comments:
  host: linuxrocks.online
  username: tcheneau
  id: 105483088210721661
---

Here the parameters are:

  • host: the FQDN of the mastodon instance you want to use
  • username: your username on this instance
  • id: the id of the toot (mastodon language for tweet)

Naturally, you might not have the id when you write you blog post: the trick is to publish your article first, without the comment system enabled, and then write a toot about it. Then, edit the YAML block at the beginning of the post and add your snippet (with the proper id). Obviously, you then re-publish the blog.

So, ready to dive in and see how it all fit in?

Overloading default theme pages

Your config.toml indicates the theme you will use. The theme will then be located in the themes/{{theme-name}} directory. The theme will control how your article are rendered. One obvious way to change how it is generated is to edit the files in this directory. But what happens when the theme upgrades? Do you overwrite your changes?

I would say: it seems better to overload the few files that are need.

Here, I created a file at the root of my Hugo repository named layouts/_default/single.html. This file is a copy of the themes/dream/layouts/_default/single.html that I edited (remember that I use the dream theme).

{{ define "title" }}
{{- .Title -}}
{{ end }}

{{ define "css" }}

{{ if .Site.Params.highlightjs }}
  {{ if .Site.Params.highlightjsTheme }}
    <link rel="stylesheet" data-highlight href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/{{ .Site.Params.highlightjsTheme }}.min.css" />
  {{ else }}
    <link rel="stylesheet" data-highlight href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/default.min.css" />
  {{ end }}
{{ end }}

{{ if .Site.Params.valine }}
  <script src='//unpkg.com/valine/dist/Valine.min.js'></script>
{{ end }}

{{ end }}

{{ define "main" }}
<div class="ui centered relaxed grid dream-grid">
  <div class="sixteen wide mobile sixteen wide tablet twelve wide computer column markdown-body dream-single" id="dream-save-post-as-img">
    {{ if and .Params.Cover .Site.Params.showSummaryCoverInPost }}
    <section class="ui top attached segment cover">
      <div class="cover-img" style="background-image: url({{ .Params.Cover }});"></div>
    </section>
    {{ end }}
    <section class="ui {{ if not .Site.Params.showSummaryCoverInPost }}top {{ end }}attached segment">
      <header>
        <h1 class="ui large header">
          {{ .Title }}
          <div class="sub header">
            @
            {{ if isset .Params "author" }}
              {{ if isset .Params "authorlink" }}
                <a href="{{ .Params.authorlink }}" target="_blank">{{ .Params.author }}</a>
              {{ else }}
                {{ .Params.author }}
              {{ end }}
            {{ else }}
              {{ .Site.Params.author }}
            {{ end }}

            | {{ if eq .Site.Language.Lang "zh" }}
                {{ index .Site.Data.zh.Weekday (printf "%d" .Date.Weekday) }},{{ index .Site.Data.zh.Month (printf "%d" .Date.Month) }} {{ .Date.Day }} 日,{{ .Date.Year }} 年
              {{ else if eq .Site.Language.Lang "es" }}
                {{ index .Site.Data.es.Weekday (printf "%d" .Date.Weekday) }}, {{ .Date.Day }} de {{ index .Site.Data.es.Month (printf "%d" .Date.Month) }} de {{ .Date.Year }}
              {{ else }}
                {{ .Date.Format "Monday, Jan 2, 2006" }}
              {{ end }}

            | {{ .ReadingTime }}{{ i18n "minuteRead" }}

            | {{ i18n "updateAt" }}
              {{ if eq .Site.Language.Lang "zh" }}
                {{ index .Site.Data.zh.Weekday (printf "%d" .Lastmod.Weekday) }},{{ index .Site.Data.zh.Month (printf "%d" .Lastmod.Month) }} {{ .Lastmod.Day }} 日,{{ .Lastmod.Year }} 年
              {{ else if eq .Site.Language.Lang "es" }}
                {{ index .Site.Data.es.Weekday (printf "%d" .Lastmod.Weekday) }}, {{ .Lastmod.Day }} de {{ index .Site.Data.es.Month (printf "%d" .Lastmod.Month) }} de {{ .Lastmod.Year }}
              {{ else }}
                {{ .Lastmod.Format "Monday, Jan 2, 2006" }}
              {{ end }}
          </div>
        </h1>
      </header>

      <article class="main">{{ .Content | emojify }}</article>
    </section>

    <footer class="ui attached segment dream-tags" data-html2canvas-ignore>
      {{ if isset .Params "tags" }}
        {{ range $tag := .Params.tags }}
          <a class="ui label" href="{{ "tags/" | relLangURL }}{{ $tag | urlize }}" title="{{ $tag }}">{{ $tag }}</a>
        {{ end }}
      {{ else }}
          <a class="ui label">{{ i18n "noTag" }}</a>
      {{ end }}
      <div
        class="ui label"
        style="float: right; cursor: pointer;"
        onclick="savePostAsImg()">
        <i class="save icon"></i>{{ i18n "saveAsImage" }}
      </div>
    </footer>

    {{ if .Site.Copyright }}
      <footer class="ui attached segment" data-html2canvas-ignore>
        {{ .Site.Copyright | safeHTML }}
      </footer>
    {{ end }}

    {{ if .Site.DisqusShortname }}
      <footer class="ui bottom attached stacked segment post-disqus-area" data-html2canvas-ignore>
        <div id="disqus_thread"></div>
        <script>
          /**
           *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
           *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables
           */

          var disqus_config = function () {
            this.page.url = '{{ .Permalink }}';  // Replace PAGE_URL with your page's canonical URL variable
            this.page.identifier = '{{ .RelPermalink }}'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
          };

          (function() { // DON'T EDIT BELOW THIS LINE
            var d = document, s = d.createElement('script');
            s.src = 'https://' + '{{ .Site.DisqusShortname }}' + '.disqus.com/embed.js';
            s.setAttribute('data-timestamp', +new Date());
            (d.head || d.body).appendChild(s);
          })();
        </script>
        <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
      </footer>
    {{ end }}
    
    {{ if .Site.Params.utterancesRepo }}
      <div class="ui segment utterances-comments" data-html2canvas-ignore>
        <script src="https://utteranc.es/client.js"
          repo="{{ .Site.Params.utterancesRepo }}"
          issue-term="og:title"
          theme="github-light"
          crossorigin="anonymous"
          async>
        </script>
      </div>
    {{ end }}

    {{ if .Site.Params.valine }}
      <div class="ui segment">
        <div id="vcomments"></div>
      </div>

      <script>
        new Valine({
          el: '#vcomments',
          appId: {{ .Site.Params.LEANCLOUD_APP_ID }},
          appKey: {{ .Site.Params.LEANCLOUD_APP_KEY }}
        })
      </script> 
    {{ end }}

  </div>
  <aside class="sixteen wide mobile sixteen wide tablet four wide computer column dream-single-aside">
    <!-- len <nav id="TableOfContents"></nav> == 32 -->
    {{ if ge (len .TableOfContents) 33 }}
      <div class="ui segment toc">
        {{ .TableOfContents }}
      </div>
    {{ end }}

    {{ partial "header.html" . }}
  </aside>
</div>
{{ end }}

{{ define "js" }}
<script src="{{ "/js/html2canvas.min.js" | relURL }}"></script>
<script src="{{ "/js/post.js" | relURL }}"></script>

{{ if .Site.Params.highlightjs }}
  <script src="{{ if .Site.Params.highlightjsCDN }}{{ .Site.Params.highlightjsCDN }}{{ else }}{{ "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js" }}{{ end}}"></script>

  {{ if .Site.Params.highlightjsExtraLanguages }}
    {{ range .Site.Params.highlightjsExtraLanguages }}
      <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/languages/{{ . }}.min.js"></script>
    {{ end }} 
  {{ end }}

  {{ partial "highlight.html" . }}
{{ end }}

{{ end }}

Here is my modified file in layouts/_default/single.html:

{{ define "title" }}
{{- .Title -}}
{{ end }}

{{ define "css" }}
<link rel="stylesheet" type="text/css" href="{{.Site.BaseURL}}css/article.css" />

{{ if .Site.Params.highlightjs }}
  {{ if .Site.Params.highlightjsTheme }}
    <link rel="stylesheet" data-highlight href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/{{ .Site.Params.highlightjsTheme }}.min.css" />
  {{ else }}
    <link rel="stylesheet" data-highlight href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/default.min.css" />
  {{ end }}
{{ end }}

{{ if .Site.Params.valine }}
  <script src='//unpkg.com/valine/dist/Valine.min.js'></script>
{{ end }}

{{ end }}

{{ define "main" }}
<div class="ui centered relaxed grid dream-grid">
  <div class="sixteen wide mobile sixteen wide tablet twelve wide computer column markdown-body dream-single" id="dream-save-post-as-img">
    {{ if and .Params.Cover .Site.Params.showSummaryCoverInPost }}
    <section class="ui top attached segment cover">
      <div class="cover-img" style="background-image: url({{ .Params.Cover }});"></div>
    </section>
    {{ end }}
    <section class="ui {{ if not .Site.Params.showSummaryCoverInPost }}top {{ end }}attached segment">
      <header>
        <h1 class="ui large header">
          {{ .Title }}
          <div class="sub header">
            @
            {{ if isset .Params "author" }}
              {{ if isset .Params "authorlink" }}
                <a href="{{ .Params.authorlink }}" target="_blank">{{ .Params.author }}</a>
              {{ else }}
                {{ .Params.author }}
              {{ end }}
            {{ else }}
              {{ .Site.Params.author }}
            {{ end }}

            | {{ if eq .Site.Language.Lang "zh" }}
                {{ index .Site.Data.zh.Weekday (printf "%d" .Date.Weekday) }},{{ index .Site.Data.zh.Month (printf "%d" .Date.Month) }} {{ .Date.Day }} 日,{{ .Date.Year }} 年
              {{ else if eq .Site.Language.Lang "es" }}
                {{ index .Site.Data.es.Weekday (printf "%d" .Date.Weekday) }}, {{ .Date.Day }} de {{ index .Site.Data.es.Month (printf "%d" .Date.Month) }} de {{ .Date.Year }}
              {{ else }}
                {{ .Date.Format "Monday, Jan 2, 2006" }}
              {{ end }}

            | {{ .ReadingTime }}{{ i18n "minuteRead" }}

            | {{ i18n "updateAt" }}
              {{ if eq .Site.Language.Lang "zh" }}
                {{ index .Site.Data.zh.Weekday (printf "%d" .Lastmod.Weekday) }},{{ index .Site.Data.zh.Month (printf "%d" .Lastmod.Month) }} {{ .Lastmod.Day }} 日,{{ .Lastmod.Year }} 年
              {{ else if eq .Site.Language.Lang "es" }}
                {{ index .Site.Data.es.Weekday (printf "%d" .Lastmod.Weekday) }}, {{ .Lastmod.Day }} de {{ index .Site.Data.es.Month (printf "%d" .Lastmod.Month) }} de {{ .Lastmod.Year }}
              {{ else }}
                {{ .Lastmod.Format "Monday, Jan 2, 2006" }}
              {{ end }}
          </div>
        </h1>
      </header>

      <article class="main">{{ .Content | emojify }}</article>
    {{ partial "article/article.html" .}}
    </section>

    <footer class="ui attached segment dream-tags" data-html2canvas-ignore>
      {{ if isset .Params "tags" }}
        {{ range $tag := .Params.tags }}
          <a class="ui label" href="{{ "tags/" | relLangURL }}{{ $tag | urlize }}" title="{{ $tag }}">{{ $tag }}</a>
        {{ end }}
      {{ else }}
          <a class="ui label">{{ i18n "noTag" }}</a>
      {{ end }}
      <div
        class="ui label"
        style="float: right; cursor: pointer;"
        onclick="savePostAsImg()">
        <i class="save icon"></i>{{ i18n "saveAsImage" }}
      </div>
    </footer>

    {{ if .Site.Copyright }}
      <footer class="ui attached segment" data-html2canvas-ignore>
        {{ .Site.Copyright | safeHTML }}
      </footer>
    {{ end }}


    {{ if .Site.DisqusShortname }}
      <footer class="ui bottom attached stacked segment post-disqus-area" data-html2canvas-ignore>
        <div id="disqus_thread"></div>
        <script>
          /**
           *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
           *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables
           */

          var disqus_config = function () {
            this.page.url = '{{ .Permalink }}';  // Replace PAGE_URL with your page's canonical URL variable
            this.page.identifier = '{{ .RelPermalink }}'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
          };

          (function() { // DON'T EDIT BELOW THIS LINE
            var d = document, s = d.createElement('script');
            s.src = 'https://' + '{{ .Site.DisqusShortname }}' + '.disqus.com/embed.js';
            s.setAttribute('data-timestamp', +new Date());
            (d.head || d.body).appendChild(s);
          })();
        </script>
        <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
      </footer>
    {{ end }}
    
    {{ if .Site.Params.utterancesRepo }}
      <div class="ui segment utterances-comments" data-html2canvas-ignore>
        <script src="https://utteranc.es/client.js"
          repo="{{ .Site.Params.utterancesRepo }}"
          issue-term="og:title"
          theme="github-light"
          crossorigin="anonymous"
          async>
        </script>
      </div>
    {{ end }}

    {{ if .Site.Params.valine }}
      <div class="ui segment">
        <div id="vcomments"></div>
      </div>

      <script>
        new Valine({
          el: '#vcomments',
          appId: {{ .Site.Params.LEANCLOUD_APP_ID }},
          appKey: {{ .Site.Params.LEANCLOUD_APP_KEY }}
        })
      </script> 
    {{ end }}

  </div>
  <aside class="sixteen wide mobile sixteen wide tablet four wide computer column dream-single-aside">
    <!-- len <nav id="TableOfContents"></nav> == 32 -->
    {{ if ge (len .TableOfContents) 33 }}
      <div class="ui segment toc">
        {{ .TableOfContents }}
      </div>
    {{ end }}

    {{ partial "header.html" . }}
  </aside>
</div>
{{ end }}

{{ define "js" }}
<script src="{{ "/js/html2canvas.min.js" | relURL }}"></script>
<script src="{{ "/js/post.js" | relURL }}"></script>

{{ if .Site.Params.highlightjs }}
  <script src="{{ if .Site.Params.highlightjsCDN }}{{ .Site.Params.highlightjsCDN }}{{ else }}{{ "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js" }}{{ end}}"></script>

  {{ if .Site.Params.highlightjsExtraLanguages }}
    {{ range .Site.Params.highlightjsExtraLanguages }}
      <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/languages/{{ . }}.min.js"></script>
    {{ end }} 
  {{ end }}

  {{ partial "highlight.html" . }}

{{ end }}

{{ end }}

If you just look at the diff, to make it more obvious:

--- themes/dream/layouts/_default/single.html	2021-01-01 18:05:35.588805563 +0100
+++ layouts/_default/single.html	2021-01-02 01:24:47.302647331 +0100
@@ -3,6 +3,7 @@
 {{ end }}
 
 {{ define "css" }}
+<link rel="stylesheet" type="text/css" href="{{.Site.BaseURL}}css/article.css" />
 
 {{ if .Site.Params.highlightjs }}
   {{ if .Site.Params.highlightjsTheme }}
@@ -65,6 +66,7 @@
       </header>
 
       <article class="main">{{ .Content | emojify }}</article>
+    {{ partial "article/article.html" .}}
     </section>

First change you see is some code to load an extra CSS file for a mysterious “article”.

Here is Carl’s static/css/article.css file:

.mastodon-comment {
    background-color: var(--body-background);
    border-radius: var(--card-border-radius);
    padding: var(--card-padding);
    margin-bottom: 1rem;
    display: flex;
    .content {
        flex-grow: 2;
    }
    .avatar img {
        margin-right: 1rem;
        min-width: 60px;
    }
    .author {
        padding-top: 0;
        display: flex;
        .date {
            margin-left: auto;
        }
    }
    .disabled {
        color: var(--accent-color)
    }
}

.mastodon-comment-content p:first-child {
    margin-top: 0;
}

The second thing you see from this diff is a partial, but what are partials ?

Partial templates

In Hugo parlance, partial template is a simple mecanism that let you extend your templating. Here, it will help us load the comments “block” when it is present among the article’s comment (the begining of the document).

Here is a partial derived from Carl’s comment system. It rests in layouts/partials/article/article.html:

{{ with .Params.comments }}
<div class="article-content">
  <h2>Comments</h2>
  <p>You can use your Mastodon account to reply to this <a class="link" href="https://{{ .host }}/@{{ .username }}/{{ .id }}">post</a>.</p>
  <p><a class="button" href="https://{{ .host }}/interact/{{ .id }}?type=reply">Reply</a></p>
  <p id="mastodon-comments-list"><button id="load-comment">Load comments</button></p>
  <noscript><p>You need JavaScript to view the comments.</p></noscript>
  <script src="/assets/js/purify.min.js"></script>
  <script type="text/javascript">
    function escapeHtml(unsafe) {
      return unsafe
           .replace(/&/g, "&amp;")
           .replace(/</g, "&lt;")
           .replace(/>/g, "&gt;")
           .replace(/"/g, "&quot;")
           .replace(/'/g, "&#039;");
   }

    document.getElementById("load-comment").addEventListener("click", function() {
      document.getElementById("load-comment").innerHTML = "Loading";
      fetch('https://{{ .host }}/api/v1/statuses/{{ .id }}/context')
        .then(function(response) {
          return response.json();
        })
        .then(function(data) {
          if(data['descendants'] &&
             Array.isArray(data['descendants']) && 
            data['descendants'].length > 0) {
              document.getElementById('mastodon-comments-list').innerHTML = "";
              data['descendants'].forEach(function(reply) {
                reply.account.display_name = escapeHtml(reply.account.display_name);
                reply.account.emojis.forEach(emoji => {
                  reply.account.display_name = reply.account.display_name.replace(`:${emoji.shortcode}:`,
                    `<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" height="20" width="20" />`);
                });
                mastodonComment =
                  `<div class="mastodon-comment">
                     <div class="avatar">
                       <img src="${escapeHtml(reply.account.avatar_static)}" height=60 width=60 alt="">
                     </div>
                     <div class="content">
                       <div class="author">
                         <a href="${reply.account.url}" rel="nofollow">
                           <span>${reply.account.display_name}</span>
                           <span class="disabled">${escapeHtml(reply.account.acct)}</span>
                         </a>
                         <a class="date" href="${reply.uri}" rel="nofollow">
                           ${reply.created_at.substr(0, 10)}
                         </a>
                       </div>
                       <div class="mastodon-comment-content">${reply.content}</div> 
                     </div>
                   </div>`;
                document.getElementById('mastodon-comments-list').appendChild(DOMPurify.sanitize(mastodonComment, {'RETURN_DOM_FRAGMENT': true}));
              });
          } else {
            document.getElementById('mastodon-comments-list').innerHTML = "<p>Not comments found</p>";
          }
        });
      });
  </script>
</div>
{{ end }}

See the first line: it only load comments when you defined the comments YAML parameter at the begining of the page.

Adding some style to your comments (with CSS)

There is a custom CSS that is needed for the comment (see Carl’s code):

.mastodon-comment {
    background-color: var(--body-background);
    border-radius: var(--card-border-radius);
    padding: var(--card-padding);
    margin-bottom: 1rem;
    display: flex;
    .content {
        flex-grow: 2;
    }
    .avatar img {
        margin-right: 1rem;
        min-width: 60px;
    }
    .author {
        padding-top: 0;
        display: flex;
        .date {
            margin-left: auto;
        }
    }
    .disabled {
        color: var(--accent-color)
    }
}
kb
.mastodon-comment-content p:first-child {
    margin-top: 0;
}

Save it as static/css/article.css.

Adding a missing dependency

Purify is a dependancy that is required by Carl’s code. It is used for sanitizing Mastodon’s comments as they are loaded.

You can download its latest version from GitHub:

wget https://raw.githubusercontent.com/cure53/DOMPurify/main/dist/purify.min.js

Copy this purify.min.js file to static/assets/js/purify.min.js.

Wrapping things up

When adding Carl’s comment system to your blog, you need to:

  • add a partial for the comment system itself
  • add the corresponding CSS
  • change the default article template to load the partial at the appropriate location
  • add the DOMPurify dependency

When writing an article:

  • link it on mastodon
  • retrieve the toot’s ID
  • add the corresponding blog to your article

Comments

You can use your Mastodon account to reply to this post.

Reply

Copyright by Tony Cheneau

Everything about $me

My name is Tony Cheneau and I’m currently a devops (catchy title) at ANSSI.

I was previously occupying a postdoc position at the National Institute of Standards and Technology (also known as NIST), in the Advanced Network Technologies Division. This was a really entertaining job where my main research interests are focused on wireless applications over the Smart Grid and defining new security solution for these applications.

If you are interested in my education (or in hiring me), you can check out my very formal (and not so up to date) resume.pdf.

How you can contact me

My previous projects

  • SimpleRPL: an implementation of the Routing Protocol for Low-Power and Lossy Networks (RFC 6550)
  • NDprotector: an implementation of the Cryptographically Generated Addresses (RFC 3972) and the Secure Neighbor Discovery Protocol (RFC 3971)
  • and more on my GitHub page

Former research interest

During my PhD, I studied several aspects of the Link-Layer security. through the extended use of the Secure Neighbor Discovery protocol (RFC 3971 and RFC 3972).

Other of my previous research interests included MANEMO. MANEMO is the combination of multiple research areas:

  • MANET (Mobile Ad-Hoc Network) specifies how new dynamic routing protocols enable mobile node to route packets over Mobile Ad-Hoc Networks.
  • NEMO (Network Mobility or Network that Moves) defines a protocol similar to Mobile IP where a whole network is moving.
  • AUTOCONF defines an addressing scheme and corresponding solutions to allocate addresses inside a MANET.

Publications

Thesis

  • Tony Cheneau, « Amélioration des adresses CGA et du protocole SEND pour un meilleur support de la mobilité et de nouveaux services de sécurité (Enhancing CGA addresses and the SEND protocol for a better support of mobility application and new security services) », January 2011 manuscript slides

Journals

  • Tony Cheneau, Aymen Boudguiga, Maryline Laurent, « Significantly improved performances of the cryptographically generated addresses thanks to ECC and GPGPU », Computers & Security journal, Elsevier, Volume 29, pages 419-431, June 2010. pdf

Conferences

  • Tony Cheneau, Ranganathan Mudumbai, « Adaptive key management for wireless sensor networks », IEEE Global Communications Conference (GLOBECOM), Atlanta, USA, December 2013.
  • Tony Cheneau, Andrei V. Sambra, Maryline Laurent, « A Trustful Authentication and Key Exchange Scheme (TAKES) for Ad Hoc Networks », 5th International Conference on Network and System Security (NSS), Milan, Italy, September 2011. pdf
  • Tony Cheneau, Maryline Laurent, « Using SEND Signature Algorithm Agility and Multiple-Key CGA to Secure Proxy Neighbor Discovery and Anycast Addressing », 6th Conference on Network Architectures and Information Systems Security, La Rochelle, France, May 2011. pdf slides
  • Tony Cheneau, Maryline Laurent, « Étude des solutions de proxy Neighbor Discovery sécurisées et proposition basée sur la Signature Agility » (a study of secure proxy Neighbor Discovery solutions and proposition of a Signature Algorithm Agility based solution) , 5ème Conférence sur la Sécurité des Architectures Réseaux et des Systèmes d’Information, Menton , France, May 2010. pdf slides
  • Tony Cheneau, Aymen Boudguiga, Maryline Laurent-Maknavicius, « Amélioration des performances des adresses CGA et du protocole SEND: étude comparée de RSA et d’ECC/ECDSA » (Improving the CGA and SEND protocol performances: a comparative study of RSA and ECC/ECDSA), 4ème Conférence sur la Sécurité des Architectures Réseaux et des Systèmes d’Information, Luchon, France, (best student paper award), pages 139-156, in proceedings (SAR-SSI 2009) (ISBN: 978-2-7483-4833-0), June 2009. pdf proceedings slides
  • Tony Cheneau, Jean-Michel Combes, Une attaque par rejeu sur le protocole SEND » (A replay attack on the SEND protocol), 3ème Conférence sur la Sécurité des Architectures Réseaux et des Systèmes d’Information, Loctudy, France, pages 289-300, in proceedings (SAR-SSI 2008) (ISBN: 978-2-7483-3289-2), October 2008. pdf proceedings slides

Research Report

  • Aymen Boudguiga, Tony Cheneau, Maryline Laurent-Maknavicius, « Usage and Performance of Cryptographically Generated Addresses » TELECOM and Management SudParis, 08-014 LOR, 2008. zip

Internet Drafts

Back in time, I made some propositions inside the CGA and SEND maIntenance working (CSI) group:

  • draft-cheneau-csi-send-sig-agility-02 proposes a Signature Agility Solution to the SEND protocol (RFC3971). link
  • draft-cheneau-csi-ecc-sig-agility-02 on the previous draft and proposes to use Elliptic Curve Cryptography in CGA (RFC 3972) and SEND (RFC 3971). link

Teachings

During my PhD, I happened to give some lecture:

  • Data network (ingénieurs 1ère année)
  • Virtual Private Network (Master 2 CCN, Master spécialisé SSR et ingénieurs 3ème année)

Education

  • 2007-2011: PhD held at the Institut Télécom SudParis under the direction of Maryline Laurent. This PhD was funded by a grant of the ANR for the MobiSEND project.
  • 2007: Master 2 SSI (sécurité des systèmes informatiques), University of Paris XII, obtained with mention bien
  • 2006: Master 1 d’informatique (STIC - F3I), University of Poitiers, obtained with mention bien
  • 2005: Licence 3 d’informatique (TIS - parcours des réseaux), University of Poitiers, obtained with mention bien
  • 2004: DEUG MIAS (mathématiques et informatique en application en science), University of Poitiers
  • 2002: Baccalauréat S Sciences de l’Ingénieur, lycée E. Branly de Châtellerault (Poitiers academy), obtained with mention assez bien