Hacker News new | past | comments | ask | show | jobs | submit login
TinySSH is a small SSH server using NaCl, TweetNaCl (tinyssh.org)
129 points by elasticdog on May 11, 2014 | hide | past | favorite | 61 comments



Outstanding! Not only are the slightly mysterious authors of this project not inventing their own crypto, they rely on djb's[0] much acclaimed NaCL/TweetNaCl. The codebase is accordingly small:

  $ wc -l source/*/*c | tail -n1
   11308 total
  $ wc -l source/crypto/*c | tail -n1
   1293 total
The first line suggests a measure of total code ballast, whereas the second incantation might hint at the amount of core crypto code. The latter might be a good starting point for any auditing endeavours.

Incidentally, I am impressed by the spirit of organisation that the source tree permeates. Both crypto/ and tinyssh/ source trees sport corresponding -test directories and a debian/ tree has already been added.

Initially, I felt irritation by the consistent lack of documentation (no README, no AUTHORS, almost no comments, it seems). Browsing the source, however, I grow convinced that this from a conviction that out-dated or redundant documentation is the greater evil.

[0] Daniel J Bernstein - author of qmail, daemontools and long-time promoter of full disclosure. https://en.wikipedia.org/wiki/Daniel_J._Bernstein


You may actually be underselling djb: He both knows how to write quality software, and is arguably the most productive cryptographer around.


this is pretty slick. i'm glad people are making more modular implementations of the programs that i depend upon in addition to creating fancy new programs that i might find useful.

for comparison, the openbsd version of openssh's sshd compiles just under 20k lines, and the openbsd version is supposed to be much smaller than the "portable" version of openssh.


How is code like below "easily auditable"?

  keydir = *++argv; if (!keydir) die_usage();
or

  if (*x == 'v') { if (flagverbose >= 2) flagverbose = 3; else flagverbose = 2; continue; }
why put multiple statements on the same line if you have nothing to hide?[1]

[1] https://www.kernel.org/doc/Documentation/CodingStyle


The answer seems to me quite simple: because they have a different coding style. Just as no one is bound to K&R or the GNU style, nothing suggests that crypto code needs to follow Linux kernel conventions.

Value consistency above everything.

Honestly, I am not convinced that the peculiar if-style isn't actually helping readability and refactoring. Note, that a Apple-style "goto break" bug might be harder to construct, when your one-line if's look like this:

   some_code;
   if (something) die(message);
   other_code;
Now the surrounding indentation does not suggest that there's an extendable block where there in fact isn't, as with Kernel style:

   some_code;
   if (something)
       die(message);
   other_code;
To be perfectly honest, the authors' style reminds me a bit of my younger self's style: keep logically connected pieces of code tightly together.

You (presumably) and I have beaten ourselves into submission to the Linux style; the authors' haven't.

On a side note: your second example even seems to suggest that the authors made some effort at additional readability by refraining from using the ternary operator construct.


"You (presumably) and I have beaten ourselves into submission to the Linux style; the authors' haven't."

An awakening!

Is it really so terrible to have things compile quickly and without the usual ./configure nonsense? It seems there are an infinite number of ways an author can organise her project using ./configure; for every one I have to spend time figuring out what they have done.

One of my favourite aspects of djb's build approach (which is what is being used here) is that it's easier (than with the popular build systems) to change compiler and linker options and make static binaries: in most cases, simple edit the conf-* files. Thankfully, authors who use djb's approach usually do not vary much from the model. This means less time spent figuring out how things are organised.

I like (portable) open source software that compiles quickly and cleanly.

djb has continued to deliver on this point.

Nice to see someone discovering this build system for the first time.


I'm just curious, where does the parent post mention anything about the build system? The "Linux style" referred to there seems to be the Linux code style, not "configure".


On top of that "configure" has nothing to do with Linux.


Who said it did? Consider that I was commenting in a general sense.

djb's work, whether it's coding style or build system or something else, tends to differ from what is "popular".

The parent's comment is along the lines of: what is unfamiliar can appear more difficult, but after the adjustment it may actually appear easier than the prior alternative.

If you view the parent's comment in this light, then you will see that both the Linux kernel coding style and the GNU build system are very familiar for lots of folks.

If you are migrating to using tinyssh and other software that follow's djb's style for the first time, then you will no doubt have to "get used to it" and you might even perceive it as being difficult.

To be crystal clear, I'm not singling out the Linux kernel or the GNU build system. The GNU build system is but one example; the Linux kernel coding style is another.

I could make a lengthy list of "familiar", popular approaches that I could argue are inferior to djb's approach to the same task.

It's just my opinion. If you disagree, feel free to state your own opinion.


> why put multiple statements on the same line if you have nothing to hide?

Because it's more readable.

Do

you

truly

find having

only a few

words per line

more

readable?

Does

it aid

with

comprehension?

I find having a high word density to be the most "readable", and it doesn't seem to matter whether it's C or English.

The TinySSH source code is small enough that I was able to read it in about 25 minutes and I learned about the SSH protocol along the way.


Note that,

that being said,

breaking a sentence across multiple lines

does help with comprehension

when you break on clause boundaries.


There is nothing particularly unauditable about that style.

To me, more code that you can get on a single screen/buffer, the better. My complaint is more about putting braces on a separate line all by themselves.


It's an SSH server. If dealing with simple cases of more than one statement on the same line is the worst bit of auditing it, it is indeed an easily auditable SSH server.

(Not that I would have written either line that way, mind you. But there's no real challenge understanding them.)


Curious as to whether something like this would be good for embedded work, as I'm working on a "modern" HP 200LX[0] running RetroBSD[1] possibly, all off a PIC micro controller (!) and want to work out how to get some form of SSH into it. Will be interesting to attempt to port it regardless of what happens though!

----

[0] http://en.wikipedia.org/wiki/HP_200LX

[1] http://retrobsd.org/wiki/doku.php


Dropbear is usually the ssh-server of choice for small systems:

https://matt.ucc.asn.au/dropbear/dropbear.html

Quick check shows that both compile to about 220K without any tuning on x86_64. While I wouldn't use that figure as serious comparison, it shows that they are in the same ballpark in size.


Someone know how it compares with dropbear [1]?

1. https://matt.ucc.asn.au/dropbear/dropbear.html


> simple configuration - TinySSH can't be misconfigured

CHALLENGE ACCEPTED!


Awesome! A few questions/thoughts:

Right now, you're suggesting it be downloaded via HTTP, which isn't exactly the best way to get my secure daemons. Any chance you could move that to HTTPS?

Semi-related: any chance you'll be making a repo available in some form? (I'm preferential to GitHub, but really anything that lets us follow source changes and open bug reports would rock)

I would love to see an audit of this by some 3rd party entity.

Glad to see folks working to build new tools from such solid building blocks!


I find use of TweetNaCl curious. For curve25519, why wouldn't one use http://code.google.com/p/curve25519-donna/ instead?

djb's cryptography is great, but djb's implementations leave something to be desired.


The usual problem with DJB's implementations is not that they're incorrect or slow or insecure -- far from it! -- but that they're awkward to integrate with the rest of the world, may require weird build configurations, and are hard for anybody else to modify. Well, TweetNaCl is really easy to integrate with the rest of the world, since it's just a single portable .c file, and the speed is surprisingly good, and its functionality and API are stable enough that you probably don't need to modify it.

So, I don't see the problem here. If these guys had tried to cobble together a replacement for NaCl out of pieces like the curve25519-donna code, that would be a problem, because there's more potential to screw that up.


I see 3 alternatives: use the original NaCl, use the unofficial fork libsodium, or use TweetNaCl. The latter shares the same authors as the original NaCl, with the advantage of being much smaller.

I don't like the fact that TinySSH modified TweetNaCl, and added back MD5:

  /*
  Based on tweetnacl 20140427 (http://tweetnacl.cr.yp.to   software.html)
  - updated int/uint types to crypto_int/crypto_uint
  - added crypto_stream_chacha20
  - added crypto_hash_sha256
  - added crypto_hash_md5
  */
I mean they use TweetNaCl because it has "state-of-the-art crypto", but then they add back MD5. Something is wrong here ...


Not sure why they added that back but the website states that

  no older cryptographic primitives - rsa, dsa, classic diffie-hellman, md5, sha1, 3des, arcfour, ...
It is actually used in the code though. I didn't look into for what it was used though.


That seems strange indeed. Some clarification is needed. Perhaps it's part of the whole NIST suite that's disabled right now?


It appears to be used only in tinysshd-printkey to print the key's fingerprint.

The fingerprint is 47 characters when printed, the key itself is 64. Since the key is so short does the fingerprint still server a useful purpose, or would it be enough to print only the key?


What do they leave to be desired? Style is a personal preference, of course - but merit? Every single djb implementation I've seen (a lot of them) is fast, efficient, secure, and clear, once you get his style.


Mostly his disregard for packaging conventions and unwillingness to acomodate them. The code is usually A grade, but the odd use of the filesystem, runtime configs, daemons etc. means it will never get into any mainstream distro without heavy patching.

There's a reason why libsodium's tag line is "P(ortable|ackageable) NaCl-based crypto library".

The word of distros/packagers isn't gospel, but it counts for a lot, considering that (for better or worse) most people won't even think about using something not available as a package.


I've been doing "apt-get install daemontools daemontools-run" since forever on Ubuntu; it might take a little longer to get into the repositories, but it does get into mainstream distros with almost no patching -- comparable, IIRC, to packages of similar size and complexity that do not have an upstream debian packaging.


Is TweetNaCl deliberately 32-bit or LLP64 only? One of the first lines is

    typedef unsigned long u32;
but on 64-bit LP64 systems (like Linux), long is 64-bits.

See http://tweetnacl.cr.yp.to/20140427/tweetnacl.c


They seem to mask u32 values just fine everywhere, so u32 being larger than it has to is no problem.

ulong is as you know the smallest type that's always guaranteed to be at least 32 bits.


No it isn't. The only guarantees you have about the size of long are that it is at least as large as (greater than or equal) a normal int. And a normal int is greater than or equal as big as a short.


C99 standard [1], §5.2.4.2.1 says otherwise. Int must acommodate at least 16 bits, long 32 bits, and long long 64 bits. C89 [2], §2.2.4.2 says the same thing but omits long long, so tweetnacl's u64 may not exist (this was the case with MSVC not too long ago).

Additionally, char is required to be at least 8 bits by the C standard, but tweetnacl assumes exactly 8. Some oddball architectures have larger character types, but POSIX mandates 8.

[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

[2] http://nepsweb.co.uk/langstand/isoC/gordon/ansi-c89w.txt


This code is attempting to be portable to C89, or why not just use C99's stdint.h? I don't believe your statement "C89 says the same thing," do you have a source to point to?

Do you have the section number in the latest freely available C99 working draft? The "Types" section (which 6.something in the draft) simply says what I said earlier about scalar rank. And 5.4.4.2.1 doesn't seem to exist in the draft.


Sorry, I mistyped the section number. Updated the parent comment with corrections and links.

I assume the intent of tweetnacl is to be C89-compatible, but due to the long long (and char, although that one's pretty pedantic) issue there's little guarantee of success.


Huh, learned something new today, thanks. (For anyone following along, the C89 doc mentions the limits.h guidelines as well, search for "UINT_MAX".)


The name is a little confusing. The way u32 is used in TweetNaCl, it doesn't have to be exactly 32 bits, only at least 32 bits.


typedef uint_least32_t u32;


Yes, TweetNaCl is. TweetNaCl's goal is to be auditable, not to be portable.

On the other hand, TinySSH actually includes a configuration mechanism to detect integer sizes, and modifies TweetNaCl accordingly, so TinySSH is not 32-bit/LLP64 only.


Are you sure? The TweetNaCl paper says u32 is an unsigned >= 32-bit (see towards the end): http://tweetnacl.cr.yp.to/papers.html


I wonder why not just use stdint.h, it's got what you need. ..


[deleted]


Yes, I did see that post. The multiple statements per line thing is really fishy for a security critical piece of infrastructure.


This is essentially just a bikeshed.

"Wait, this code is hard to understand and requires deep domain knowledge. Better nitpick the code style instead. Also, bikesheds should clearly be orange. Green is way too fishy."


I have domain knowledge and I've read a lot of code in my time. The style in use there is peculiar. That doesn't mean it is malicious or incorrect but it might make it easier to hide if it was.

Sorry you don't see that.


stdint.h is C99 and while I'd love to believe that every C compiler is C99 capable by now, I don't know if that is actually the case. Does stdint.h exist on Win32 these days?


Microsoft's poor support for standard C is no excuse not to use these types everywhere else and either define them yourself on windows or get stdint from boost, from one of the stdint.h replacements or <cstdint> from MSVC 2012.

IMHO, of course.


I hate Microsoft as much as the next guy, but FYI, MSVC 2013 supports most of C99.


Maybe by Microsoft's definition of 'most'.

* no variable-length arrays

* no qualifiers in parameter array declarators (`int x[static 10]`, etc.)

* no `restrict` keyword

* no compound literals

* no designated initializers

There's probably more.

On the web you'll find the same quote copy & pasted over and over saying that support for compound literals and designated initializers was supposedly added in VS2013, but it does not appear to be true. Either that, or I haven't found the hidden switch to enable it. By the way, C code still needs to be compiled as C++ to get anything beyond C89 to work, which should give you a clue as to how serious Microsoft is about C99.

What's true though is that stdbool.h was added. It's a start, I guess...


That's good to hear, and maybe we can finally move on to more universal use of a 14 year old standard!


What use is an sshd that doesn't support SCP? I think to most people that is a core feature, I'd be surprised if it wasn't a requirement for git for example.

It sounds like it's small enough perhaps for a direct port to a safe language like rust, that would be interesting (to me at least).


To be fair, OpenSSH's sshd doesn't have support for scp either. scp invokes ssh to connect to the target host and launches another scp instance there to talk to. There's no reason that wouldn't work with TinySSH too.


Use this as a poor man's scp:

tar c path/to/files | ssh host tar x


Git doesn't require scp.


This should work perfectly well with rsync -e ssh. Which is what you should use anyway.


Rsync stats a bunch of files on both sides. There are plenty of cases where reading is much more expensive than writing at the destination---imagine Glacier, maybe.


Sure but I don't think you can scp to Glacier either.


Rob Landley doesn't seems to be too excited about it http://www.landley.net/notes.html#31-03-2014


Which ssh clients can connect to this? For example, can putty connect to this?


Only 6.5/6.6 versions of the OpenSSH client can connect to this server. 6.5 is the first (and as far as I know only) ssh client that introduced chacha20 and ed25519 support.


Every function name in tinyssh/buf.c starts with an underscore...

To quote the standard:

"All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces. ... If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined."

-- ISO/IEC 9899:1999, Section 7.1.3 Reserved Identifiers


don't we already have dropbear for such tasks, would it be better to "extend" / "improve" dropbear?


Daemontools and inetd? May I ask where the systemd socket activation support is?


It's easy, and requires no modification to TinySSH. See here:

http://0pointer.de/blog/projects/inetd.html




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: