Initialisierung

This commit is contained in:
2020-04-19 14:49:21 +02:00
parent 3fdd1b36fc
commit 9fd48d50e3
293 changed files with 38035 additions and 0 deletions

0
lib/CMakeLists.txt Normal file → Executable file
View File

106
lib/byteutils.c Executable file
View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <time.h>
#include <netinet/in.h>
#include "byteutils.h"
#ifndef htonll
#include <endian.h>
#define htonll(x) htobe64(x)
#define ntohll(x) be64toh(x)
#endif
// The functions in this file assume a little endian cpu architecture!
/**
* Reads a little endian unsigned 16 bit integer from the buffer at position offset
*/
uint16_t byteutils_get_short(unsigned char* b, int offset) {
return *((uint16_t*)(b + offset));
}
/**
* Reads a little endian unsigned 32 bit integer from the buffer at position offset
*/
uint32_t byteutils_get_int(unsigned char* b, int offset) {
return *((uint32_t*)(b + offset));
}
/**
* Reads a little endian unsigned 64 bit integer from the buffer at position offset
*/
uint64_t byteutils_get_long(unsigned char* b, int offset) {
return *((uint64_t*)(b + offset));
}
/**
* Reads a big endian unsigned 16 bit integer from the buffer at position offset
*/
uint16_t byteutils_get_short_be(unsigned char* b, int offset) {
return ntohs(byteutils_get_short(b, offset));
}
/**
* Reads a big endian unsigned 32 bit integer from the buffer at position offset
*/
uint32_t byteutils_get_int_be(unsigned char* b, int offset) {
return ntohl(byteutils_get_int(b, offset));
}
/**
* Reads a big endian unsigned 64 bit integer from the buffer at position offset
*/
uint64_t byteutils_get_long_be(unsigned char* b, int offset) {
return ntohll(byteutils_get_long(b, offset));
}
/**
* Reads a float from the buffer at position offset
*/
float byteutils_get_float(unsigned char* b, int offset) {
return *((float*)(b + offset));
}
/**
* Writes a little endian unsigned 32 bit integer to the buffer at position offset
*/
void byteutils_put_int(unsigned char* b, int offset, uint32_t value) {
*((uint32_t*)(b + offset)) = value;
}
/**
* Reads an ntp timestamp and returns it as micro seconds since the Unix epoch
*/
uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset) {
uint64_t seconds = ntohl(((unsigned int) byteutils_get_int(b, offset))) - SECONDS_FROM_1900_TO_1970;
uint64_t fraction = ntohl((unsigned int) byteutils_get_int(b, offset + 4));
return (seconds * 1000000L) + ((fraction * 1000000L) >> 32);
}
/**
* Writes a time given as micro seconds since the Unix time epoch as an ntp timestamp
* into the buffer at position offset
*/
void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970) {
uint64_t seconds = us_since_1970 / 1000000L;
uint64_t microseconds = us_since_1970 % 1000000L;
seconds += SECONDS_FROM_1900_TO_1970;
uint64_t fraction = (microseconds << 32) / 1000000L;
// Write in big endian!
byteutils_put_int(b, offset, htonl(seconds));
byteutils_put_int(b, offset + 4, htonl(fraction));
}

32
lib/byteutils.h Executable file
View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef AIRPLAYSERVER_BYTEUTILS_H
#define AIRPLAYSERVER_BYTEUTILS_H
#include <stdint.h>
uint16_t byteutils_get_short(unsigned char* b, int offset);
uint32_t byteutils_get_int(unsigned char* b, int offset);
uint64_t byteutils_get_long(unsigned char* b, int offset);
uint16_t byteutils_get_short_be(unsigned char* b, int offset);
uint32_t byteutils_get_int_be(unsigned char* b, int offset);
uint64_t byteutils_get_long_be(unsigned char* b, int offset);
float byteutils_get_float(unsigned char* b, int offset);
#define SECONDS_FROM_1900_TO_1970 2208988800ULL
uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset);
void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970);
#endif //AIRPLAYSERVER_BYTEUTILS_H

41
lib/compat.h Executable file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef COMPAT_H
#define COMPAT_H
#if defined(WIN32)
#include <ws2tcpip.h>
#include <windows.h>
#ifndef snprintf
#define snprintf _snprintf
#endif
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <pthread.h>
#endif
#include "memalign.h"
#include "sockets.h"
#include "threads.h"
#endif

209
lib/crypto.c Normal file
View File

@@ -0,0 +1,209 @@
/**
* RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi
* Copyright (C) 2019 Florian Draschbacher
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "crypto.h"
#include <openssl/evp.h>
#include <openssl/err.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
struct aes_ctx_s {
EVP_CIPHER_CTX *cipher_ctx;
uint8_t key[AES_128_BLOCK_SIZE];
uint8_t iv[AES_128_BLOCK_SIZE];
aes_direction_t direction;
uint8_t block_offset;
};
uint8_t waste[AES_128_BLOCK_SIZE];
// Common AES utilities
void handle_error(const char* location) {
long error = ERR_get_error();
const char* error_str = ERR_error_string(error, NULL);
printf("Crypto error at %s: %s\n", location, error_str);
assert(false);
}
aes_ctx_t *aes_init(const uint8_t *key, const uint8_t *iv, const EVP_CIPHER *type, aes_direction_t direction) {
aes_ctx_t *ctx = malloc(sizeof(aes_ctx_t));
assert(ctx != NULL);
ctx->cipher_ctx = EVP_CIPHER_CTX_new();
assert(ctx->cipher_ctx != NULL);
ctx->block_offset = 0;
ctx->direction = direction;
if (direction == AES_ENCRYPT) {
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
handle_error(__func__);
}
} else {
if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
handle_error(__func__);
}
}
memcpy(ctx->key, key, AES_128_BLOCK_SIZE);
memcpy(ctx->iv, iv, AES_128_BLOCK_SIZE);
return ctx;
}
void aes_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) {
int out_len = 0;
if (!EVP_EncryptUpdate(ctx->cipher_ctx, out, &out_len, in, in_len)) {
handle_error(__func__);
}
assert(out_len <= in_len);
}
void aes_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) {
int out_len = 0;
if (!EVP_DecryptUpdate(ctx->cipher_ctx, out, &out_len, in, in_len)) {
handle_error(__func__);
}
assert(out_len <= in_len);
}
void aes_destroy(aes_ctx_t *ctx) {
if (ctx) {
EVP_CIPHER_CTX_free(ctx->cipher_ctx);
free(ctx);
}
}
void aes_reset(aes_ctx_t *ctx, const EVP_CIPHER *type, aes_direction_t direction) {
if (!EVP_CIPHER_CTX_reset(ctx->cipher_ctx)) {
handle_error(__func__);
}
if (direction == AES_ENCRYPT) {
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, ctx->key, ctx->iv)) {
handle_error(__func__);
}
} else {
if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, ctx->key, ctx->iv)) {
handle_error(__func__);
}
}
}
// AES CTR
aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv) {
return aes_init(key, iv, EVP_aes_128_ctr(), AES_ENCRYPT);
}
void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
aes_encrypt(ctx, in, out, len);
ctx->block_offset = (ctx->block_offset + len) % AES_128_BLOCK_SIZE;
}
void aes_ctr_start_fresh_block(aes_ctx_t *ctx) {
// Is there a better way to do this?
if (ctx->block_offset == 0) return;
aes_ctr_encrypt(ctx, waste, waste, AES_128_BLOCK_SIZE - ctx->block_offset);
}
void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
aes_encrypt(ctx, in, out, len);
}
void aes_ctr_reset(aes_ctx_t *ctx) {
aes_reset(ctx, EVP_aes_128_ctr(), AES_ENCRYPT);
}
void aes_ctr_destroy(aes_ctx_t *ctx) {
aes_destroy(ctx);
}
// AES CBC
aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction) {
return aes_init(key, iv, EVP_aes_128_cbc(), direction);
}
void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
assert(ctx->direction == AES_ENCRYPT);
aes_encrypt(ctx, in, out, len);
}
void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
assert(ctx->direction == AES_DECRYPT);
aes_decrypt(ctx, in, out, len);
}
void aes_cbc_reset(aes_ctx_t *ctx) {
aes_reset(ctx, EVP_aes_128_ctr(), ctx->direction);
}
void aes_cbc_destroy(aes_ctx_t *ctx) {
aes_destroy(ctx);
}
// SHA 512
struct sha_ctx_s {
EVP_MD_CTX *digest_ctx;
};
sha_ctx_t *sha_init() {
sha_ctx_t *ctx = malloc(sizeof(sha_ctx_t));
assert(ctx != NULL);
ctx->digest_ctx = EVP_MD_CTX_new();
assert(ctx->digest_ctx != NULL);
if (!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
handle_error(__func__);
}
return ctx;
}
void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len) {
if (!EVP_DigestUpdate(ctx->digest_ctx, in, len)) {
handle_error(__func__);
}
}
void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len) {
if (!EVP_DigestFinal_ex(ctx->digest_ctx, out, len)) {
handle_error(__func__);
}
}
void sha_reset(sha_ctx_t *ctx) {
if (!EVP_MD_CTX_reset(ctx->digest_ctx) ||
!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
handle_error(__func__);
}
}
void sha_destroy(sha_ctx_t *ctx) {
if (ctx) {
EVP_MD_CTX_free(ctx->digest_ctx);
free(ctx);
}
}

67
lib/crypto.h Normal file
View File

@@ -0,0 +1,67 @@
/**
* RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi
* Copyright (C) 2019 Florian Draschbacher
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Helper methods for various crypto operations.
* Uses OpenSSL behind the scenes.
*/
#ifndef CRYPTO_H
#define CRYPTO_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// 128bit AES in CTR mode
#define AES_128_BLOCK_SIZE 16
typedef enum aes_direction_e { AES_DECRYPT, AES_ENCRYPT } aes_direction_t;
typedef struct aes_ctx_s aes_ctx_t;
aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv);
void aes_ctr_reset(aes_ctx_t *ctx);
void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
void aes_ctr_start_fresh_block(aes_ctx_t *ctx);
void aes_ctr_destroy(aes_ctx_t *ctx);
aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction);
void aes_cbc_reset(aes_ctx_t *ctx);
void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
void aes_cbc_destroy(aes_ctx_t *ctx);
// SHA512
typedef struct sha_ctx_s sha_ctx_t;
sha_ctx_t *sha_init();
void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len);
void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len);
void sha_reset(sha_ctx_t *ctx);
void sha_destroy(sha_ctx_t *ctx);
#ifdef __cplusplus
}
#endif
#endif

6
lib/curve25519/CMakeLists.txt Executable file
View File

@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. curve25519_src)
set(DIR_SRCS ${curve25519_src})
add_library( curve25519
STATIC
${DIR_SRCS})

860
lib/curve25519/curve25519-donna.c Executable file
View File

@@ -0,0 +1,860 @@
/* Copyright 2008, Google Inc.
* 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 Google Inc. 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.
*
* curve25519-donna: Curve25519 elliptic curve, public key function
*
* http://code.google.com/p/curve25519-donna/
*
* Adam Langley <agl@imperialviolet.org>
*
* Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
*
* More information about curve25519 can be found here
* http://cr.yp.to/ecdh.html
*
* djb's sample implementation of curve25519 is written in a special assembly
* language called qhasm and uses the floating point registers.
*
* This is, almost, a clean room reimplementation from the curve25519 paper. It
* uses many of the tricks described therein. Only the crecip function is taken
* from the sample implementation. */
#include <string.h>
#include <stdint.h>
#ifdef _MSC_VER
#define inline __inline
#endif
typedef uint8_t u8;
typedef int32_t s32;
typedef int64_t limb;
/* Field element representation:
*
* Field elements are written as an array of signed, 64-bit limbs, least
* significant first. The value of the field element is:
* x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
*
* i.e. the limbs are 26, 25, 26, 25, ... bits wide. */
/* Sum two numbers: output += in */
static void fsum(limb *output, const limb *in) {
unsigned i;
for (i = 0; i < 10; i += 2) {
output[0+i] = output[0+i] + in[0+i];
output[1+i] = output[1+i] + in[1+i];
}
}
/* Find the difference of two numbers: output = in - output
* (note the order of the arguments!). */
static void fdifference(limb *output, const limb *in) {
unsigned i;
for (i = 0; i < 10; ++i) {
output[i] = in[i] - output[i];
}
}
/* Multiply a number by a scalar: output = in * scalar */
static void fscalar_product(limb *output, const limb *in, const limb scalar) {
unsigned i;
for (i = 0; i < 10; ++i) {
output[i] = in[i] * scalar;
}
}
/* Multiply two numbers: output = in2 * in
*
* output must be distinct to both inputs. The inputs are reduced coefficient
* form, the output is not.
*
* output[x] <= 14 * the largest product of the input limbs. */
static void fproduct(limb *output, const limb *in2, const limb *in) {
output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);
output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) +
((limb) ((s32) in2[1])) * ((s32) in[0]);
output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[2]) +
((limb) ((s32) in2[2])) * ((s32) in[0]);
output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) +
((limb) ((s32) in2[2])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[3]) +
((limb) ((s32) in2[3])) * ((s32) in[0]);
output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) +
2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) +
((limb) ((s32) in2[3])) * ((s32) in[1])) +
((limb) ((s32) in2[0])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[0]);
output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) +
((limb) ((s32) in2[3])) * ((s32) in[2]) +
((limb) ((s32) in2[1])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[0]);
output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) +
((limb) ((s32) in2[1])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[1])) +
((limb) ((s32) in2[2])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[2]) +
((limb) ((s32) in2[0])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[0]);
output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) +
((limb) ((s32) in2[4])) * ((s32) in[3]) +
((limb) ((s32) in2[2])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[2]) +
((limb) ((s32) in2[1])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[0]);
output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) +
2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[3]) +
((limb) ((s32) in2[1])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[1])) +
((limb) ((s32) in2[2])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[2]) +
((limb) ((s32) in2[0])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[0]);
output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) +
((limb) ((s32) in2[5])) * ((s32) in[4]) +
((limb) ((s32) in2[3])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[3]) +
((limb) ((s32) in2[2])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[2]) +
((limb) ((s32) in2[1])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[1]) +
((limb) ((s32) in2[0])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[0]);
output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) +
((limb) ((s32) in2[3])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[3]) +
((limb) ((s32) in2[1])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[1])) +
((limb) ((s32) in2[4])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[4]) +
((limb) ((s32) in2[2])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[2]);
output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) +
((limb) ((s32) in2[6])) * ((s32) in[5]) +
((limb) ((s32) in2[4])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[4]) +
((limb) ((s32) in2[3])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[3]) +
((limb) ((s32) in2[2])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[2]);
output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) +
2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[5]) +
((limb) ((s32) in2[3])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[3])) +
((limb) ((s32) in2[4])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[4]);
output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) +
((limb) ((s32) in2[7])) * ((s32) in[6]) +
((limb) ((s32) in2[5])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[5]) +
((limb) ((s32) in2[4])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[4]);
output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) +
((limb) ((s32) in2[5])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[5])) +
((limb) ((s32) in2[6])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[6]);
output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) +
((limb) ((s32) in2[8])) * ((s32) in[7]) +
((limb) ((s32) in2[6])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[6]);
output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) +
2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[7]));
output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) +
((limb) ((s32) in2[9])) * ((s32) in[8]);
output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);
}
/* Reduce a long form to a short form by taking the input mod 2^255 - 19.
*
* On entry: |output[i]| < 14*2^54
* On exit: |output[0..8]| < 280*2^54 */
static void freduce_degree(limb *output) {
/* Each of these shifts and adds ends up multiplying the value by 19.
*
* For output[0..8], the absolute entry value is < 14*2^54 and we add, at
* most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */
output[8] += output[18] << 4;
output[8] += output[18] << 1;
output[8] += output[18];
output[7] += output[17] << 4;
output[7] += output[17] << 1;
output[7] += output[17];
output[6] += output[16] << 4;
output[6] += output[16] << 1;
output[6] += output[16];
output[5] += output[15] << 4;
output[5] += output[15] << 1;
output[5] += output[15];
output[4] += output[14] << 4;
output[4] += output[14] << 1;
output[4] += output[14];
output[3] += output[13] << 4;
output[3] += output[13] << 1;
output[3] += output[13];
output[2] += output[12] << 4;
output[2] += output[12] << 1;
output[2] += output[12];
output[1] += output[11] << 4;
output[1] += output[11] << 1;
output[1] += output[11];
output[0] += output[10] << 4;
output[0] += output[10] << 1;
output[0] += output[10];
}
#if (-1 & 3) != 3
#error "This code only works on a two's complement system"
#endif
/* return v / 2^26, using only shifts and adds.
*
* On entry: v can take any value. */
static inline limb
div_by_2_26(const limb v)
{
/* High word of v; no shift needed. */
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
/* Set to all 1s if v was negative; else set to 0s. */
const int32_t sign = ((int32_t) highword) >> 31;
/* Set to 0x3ffffff if v was negative; else set to 0. */
const int32_t roundoff = ((uint32_t) sign) >> 6;
/* Should return v / (1<<26) */
return (v + roundoff) >> 26;
}
/* return v / (2^25), using only shifts and adds.
*
* On entry: v can take any value. */
static inline limb
div_by_2_25(const limb v)
{
/* High word of v; no shift needed*/
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
/* Set to all 1s if v was negative; else set to 0s. */
const int32_t sign = ((int32_t) highword) >> 31;
/* Set to 0x1ffffff if v was negative; else set to 0. */
const int32_t roundoff = ((uint32_t) sign) >> 7;
/* Should return v / (1<<25) */
return (v + roundoff) >> 25;
}
/* Reduce all coefficients of the short form input so that |x| < 2^26.
*
* On entry: |output[i]| < 280*2^54 */
static void freduce_coefficients(limb *output) {
unsigned i;
output[10] = 0;
for (i = 0; i < 10; i += 2) {
limb over = div_by_2_26(output[i]);
/* The entry condition (that |output[i]| < 280*2^54) means that over is, at
* most, 280*2^28 in the first iteration of this loop. This is added to the
* next limb and we can approximate the resulting bound of that limb by
* 281*2^54. */
output[i] -= over << 26;
output[i+1] += over;
/* For the first iteration, |output[i+1]| < 281*2^54, thus |over| <
* 281*2^29. When this is added to the next limb, the resulting bound can
* be approximated as 281*2^54.
*
* For subsequent iterations of the loop, 281*2^54 remains a conservative
* bound and no overflow occurs. */
over = div_by_2_25(output[i+1]);
output[i+1] -= over << 25;
output[i+2] += over;
}
/* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */
output[0] += output[10] << 4;
output[0] += output[10] << 1;
output[0] += output[10];
output[10] = 0;
/* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29
* So |over| will be no more than 2^16. */
{
limb over = div_by_2_26(output[0]);
output[0] -= over << 26;
output[1] += over;
}
/* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The
* bound on |output[1]| is sufficient to meet our needs. */
}
/* A helpful wrapper around fproduct: output = in * in2.
*
* On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.
*
* output must be distinct to both inputs. The output is reduced degree
* (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */
static void
fmul(limb *output, const limb *in, const limb *in2) {
limb t[19];
fproduct(t, in, in2);
/* |t[i]| < 14*2^54 */
freduce_degree(t);
freduce_coefficients(t);
/* |t[i]| < 2^26 */
memcpy(output, t, sizeof(limb) * 10);
}
/* Square a number: output = in**2
*
* output must be distinct from the input. The inputs are reduced coefficient
* form, the output is not.
*
* output[x] <= 14 * the largest product of the input limbs. */
static void fsquare_inner(limb *output, const limb *in) {
output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);
output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);
output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) +
((limb) ((s32) in[0])) * ((s32) in[2]));
output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) +
((limb) ((s32) in[0])) * ((s32) in[3]));
output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) +
4 * ((limb) ((s32) in[1])) * ((s32) in[3]) +
2 * ((limb) ((s32) in[0])) * ((s32) in[4]);
output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) +
((limb) ((s32) in[1])) * ((s32) in[4]) +
((limb) ((s32) in[0])) * ((s32) in[5]));
output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) +
((limb) ((s32) in[2])) * ((s32) in[4]) +
((limb) ((s32) in[0])) * ((s32) in[6]) +
2 * ((limb) ((s32) in[1])) * ((s32) in[5]));
output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) +
((limb) ((s32) in[2])) * ((s32) in[5]) +
((limb) ((s32) in[1])) * ((s32) in[6]) +
((limb) ((s32) in[0])) * ((s32) in[7]));
output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) +
2 * (((limb) ((s32) in[2])) * ((s32) in[6]) +
((limb) ((s32) in[0])) * ((s32) in[8]) +
2 * (((limb) ((s32) in[1])) * ((s32) in[7]) +
((limb) ((s32) in[3])) * ((s32) in[5])));
output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) +
((limb) ((s32) in[3])) * ((s32) in[6]) +
((limb) ((s32) in[2])) * ((s32) in[7]) +
((limb) ((s32) in[1])) * ((s32) in[8]) +
((limb) ((s32) in[0])) * ((s32) in[9]));
output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) +
((limb) ((s32) in[4])) * ((s32) in[6]) +
((limb) ((s32) in[2])) * ((s32) in[8]) +
2 * (((limb) ((s32) in[3])) * ((s32) in[7]) +
((limb) ((s32) in[1])) * ((s32) in[9])));
output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) +
((limb) ((s32) in[4])) * ((s32) in[7]) +
((limb) ((s32) in[3])) * ((s32) in[8]) +
((limb) ((s32) in[2])) * ((s32) in[9]));
output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) +
2 * (((limb) ((s32) in[4])) * ((s32) in[8]) +
2 * (((limb) ((s32) in[5])) * ((s32) in[7]) +
((limb) ((s32) in[3])) * ((s32) in[9])));
output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) +
((limb) ((s32) in[5])) * ((s32) in[8]) +
((limb) ((s32) in[4])) * ((s32) in[9]));
output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) +
((limb) ((s32) in[6])) * ((s32) in[8]) +
2 * ((limb) ((s32) in[5])) * ((s32) in[9]));
output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) +
((limb) ((s32) in[6])) * ((s32) in[9]));
output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) +
4 * ((limb) ((s32) in[7])) * ((s32) in[9]);
output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]);
output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);
}
/* fsquare sets output = in^2.
*
* On entry: The |in| argument is in reduced coefficients form and |in[i]| <
* 2^27.
*
* On exit: The |output| argument is in reduced coefficients form (indeed, one
* need only provide storage for 10 limbs) and |out[i]| < 2^26. */
static void
fsquare(limb *output, const limb *in) {
limb t[19];
fsquare_inner(t, in);
/* |t[i]| < 14*2^54 because the largest product of two limbs will be <
* 2^(27+27) and fsquare_inner adds together, at most, 14 of those
* products. */
freduce_degree(t);
freduce_coefficients(t);
/* |t[i]| < 2^26 */
memcpy(output, t, sizeof(limb) * 10);
}
/* Take a little-endian, 32-byte number and expand it into polynomial form */
static void
fexpand(limb *output, const u8 *input) {
#define F(n,start,shift,mask) \
output[n] = ((((limb) input[start + 0]) | \
((limb) input[start + 1]) << 8 | \
((limb) input[start + 2]) << 16 | \
((limb) input[start + 3]) << 24) >> shift) & mask;
F(0, 0, 0, 0x3ffffff);
F(1, 3, 2, 0x1ffffff);
F(2, 6, 3, 0x3ffffff);
F(3, 9, 5, 0x1ffffff);
F(4, 12, 6, 0x3ffffff);
F(5, 16, 0, 0x1ffffff);
F(6, 19, 1, 0x3ffffff);
F(7, 22, 3, 0x1ffffff);
F(8, 25, 4, 0x3ffffff);
F(9, 28, 6, 0x1ffffff);
#undef F
}
#if (-32 >> 1) != -16
#error "This code only works when >> does sign-extension on negative numbers"
#endif
/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */
static s32 s32_eq(s32 a, s32 b) {
a = ~(a ^ b);
a &= a << 16;
a &= a << 8;
a &= a << 4;
a &= a << 2;
a &= a << 1;
return a >> 31;
}
/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are
* both non-negative. */
static s32 s32_gte(s32 a, s32 b) {
a -= b;
/* a >= 0 iff a >= b. */
return ~(a >> 31);
}
/* Take a fully reduced polynomial form number and contract it into a
* little-endian, 32-byte array.
*
* On entry: |input_limbs[i]| < 2^26 */
static void
fcontract(u8 *output, limb *input_limbs) {
int i;
int j;
s32 input[10];
s32 mask;
/* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */
for (i = 0; i < 10; i++) {
input[i] = input_limbs[i];
}
for (j = 0; j < 2; ++j) {
for (i = 0; i < 9; ++i) {
if ((i & 1) == 1) {
/* This calculation is a time-invariant way to make input[i]
* non-negative by borrowing from the next-larger limb. */
const s32 mask = input[i] >> 31;
const s32 carry = -((input[i] & mask) >> 25);
input[i] = input[i] + (carry << 25);
input[i+1] = input[i+1] - carry;
} else {
const s32 mask = input[i] >> 31;
const s32 carry = -((input[i] & mask) >> 26);
input[i] = input[i] + (carry << 26);
input[i+1] = input[i+1] - carry;
}
}
/* There's no greater limb for input[9] to borrow from, but we can multiply
* by 19 and borrow from input[0], which is valid mod 2^255-19. */
{
const s32 mask = input[9] >> 31;
const s32 carry = -((input[9] & mask) >> 25);
input[9] = input[9] + (carry << 25);
input[0] = input[0] - (carry * 19);
}
/* After the first iteration, input[1..9] are non-negative and fit within
* 25 or 26 bits, depending on position. However, input[0] may be
* negative. */
}
/* The first borrow-propagation pass above ended with every limb
except (possibly) input[0] non-negative.
If input[0] was negative after the first pass, then it was because of a
carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most,
one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19.
In the second pass, each limb is decreased by at most one. Thus the second
borrow-propagation pass could only have wrapped around to decrease
input[0] again if the first pass left input[0] negative *and* input[1]
through input[9] were all zero. In that case, input[1] is now 2^25 - 1,
and this last borrow-propagation step will leave input[1] non-negative. */
{
const s32 mask = input[0] >> 31;
const s32 carry = -((input[0] & mask) >> 26);
input[0] = input[0] + (carry << 26);
input[1] = input[1] - carry;
}
/* All input[i] are now non-negative. However, there might be values between
* 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */
for (j = 0; j < 2; j++) {
for (i = 0; i < 9; i++) {
if ((i & 1) == 1) {
const s32 carry = input[i] >> 25;
input[i] &= 0x1ffffff;
input[i+1] += carry;
} else {
const s32 carry = input[i] >> 26;
input[i] &= 0x3ffffff;
input[i+1] += carry;
}
}
{
const s32 carry = input[9] >> 25;
input[9] &= 0x1ffffff;
input[0] += 19*carry;
}
}
/* If the first carry-chain pass, just above, ended up with a carry from
* input[9], and that caused input[0] to be out-of-bounds, then input[0] was
* < 2^26 + 2*19, because the carry was, at most, two.
*
* If the second pass carried from input[9] again then input[0] is < 2*19 and
* the input[9] -> input[0] carry didn't push input[0] out of bounds. */
/* It still remains the case that input might be between 2^255-19 and 2^255.
* In this case, input[1..9] must take their maximum value and input[0] must
* be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
mask = s32_gte(input[0], 0x3ffffed);
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
mask &= s32_eq(input[i], 0x1ffffff);
} else {
mask &= s32_eq(input[i], 0x3ffffff);
}
}
/* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus
* this conditionally subtracts 2^255-19. */
input[0] -= mask & 0x3ffffed;
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
input[i] -= mask & 0x1ffffff;
} else {
input[i] -= mask & 0x3ffffff;
}
}
input[1] <<= 2;
input[2] <<= 3;
input[3] <<= 5;
input[4] <<= 6;
input[6] <<= 1;
input[7] <<= 3;
input[8] <<= 4;
input[9] <<= 6;
#define F(i, s) \
output[s+0] |= input[i] & 0xff; \
output[s+1] = (input[i] >> 8) & 0xff; \
output[s+2] = (input[i] >> 16) & 0xff; \
output[s+3] = (input[i] >> 24) & 0xff;
output[0] = 0;
output[16] = 0;
F(0,0);
F(1,3);
F(2,6);
F(3,9);
F(4,12);
F(5,16);
F(6,19);
F(7,22);
F(8,25);
F(9,28);
#undef F
}
/* Input: Q, Q', Q-Q'
* Output: 2Q, Q+Q'
*
* x2 z3: long form
* x3 z3: long form
* x z: short form, destroyed
* xprime zprime: short form, destroyed
* qmqp: short form, preserved
*
* On entry and exit, the absolute value of the limbs of all inputs and outputs
* are < 2^26. */
static void fmonty(limb *x2, limb *z2, /* output 2Q */
limb *x3, limb *z3, /* output Q + Q' */
limb *x, limb *z, /* input Q */
limb *xprime, limb *zprime, /* input Q' */
const limb *qmqp /* input Q - Q' */) {
limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
zzprime[19], zzzprime[19], xxxprime[19];
memcpy(origx, x, 10 * sizeof(limb));
fsum(x, z);
/* |x[i]| < 2^27 */
fdifference(z, origx); /* does x - z */
/* |z[i]| < 2^27 */
memcpy(origxprime, xprime, sizeof(limb) * 10);
fsum(xprime, zprime);
/* |xprime[i]| < 2^27 */
fdifference(zprime, origxprime);
/* |zprime[i]| < 2^27 */
fproduct(xxprime, xprime, z);
/* |xxprime[i]| < 14*2^54: the largest product of two limbs will be <
* 2^(27+27) and fproduct adds together, at most, 14 of those products.
* (Approximating that to 2^58 doesn't work out.) */
fproduct(zzprime, x, zprime);
/* |zzprime[i]| < 14*2^54 */
freduce_degree(xxprime);
freduce_coefficients(xxprime);
/* |xxprime[i]| < 2^26 */
freduce_degree(zzprime);
freduce_coefficients(zzprime);
/* |zzprime[i]| < 2^26 */
memcpy(origxprime, xxprime, sizeof(limb) * 10);
fsum(xxprime, zzprime);
/* |xxprime[i]| < 2^27 */
fdifference(zzprime, origxprime);
/* |zzprime[i]| < 2^27 */
fsquare(xxxprime, xxprime);
/* |xxxprime[i]| < 2^26 */
fsquare(zzzprime, zzprime);
/* |zzzprime[i]| < 2^26 */
fproduct(zzprime, zzzprime, qmqp);
/* |zzprime[i]| < 14*2^52 */
freduce_degree(zzprime);
freduce_coefficients(zzprime);
/* |zzprime[i]| < 2^26 */
memcpy(x3, xxxprime, sizeof(limb) * 10);
memcpy(z3, zzprime, sizeof(limb) * 10);
fsquare(xx, x);
/* |xx[i]| < 2^26 */
fsquare(zz, z);
/* |zz[i]| < 2^26 */
fproduct(x2, xx, zz);
/* |x2[i]| < 14*2^52 */
freduce_degree(x2);
freduce_coefficients(x2);
/* |x2[i]| < 2^26 */
fdifference(zz, xx); // does zz = xx - zz
/* |zz[i]| < 2^27 */
memset(zzz + 10, 0, sizeof(limb) * 9);
fscalar_product(zzz, zz, 121665);
/* |zzz[i]| < 2^(27+17) */
/* No need to call freduce_degree here:
fscalar_product doesn't increase the degree of its input. */
freduce_coefficients(zzz);
/* |zzz[i]| < 2^26 */
fsum(zzz, xx);
/* |zzz[i]| < 2^27 */
fproduct(z2, zz, zzz);
/* |z2[i]| < 14*2^(26+27) */
freduce_degree(z2);
freduce_coefficients(z2);
/* |z2|i| < 2^26 */
}
/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
* them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid
* side-channel attacks.
*
* NOTE that this function requires that 'iswap' be 1 or 0; other values give
* wrong results. Also, the two limb arrays must be in reduced-coefficient,
* reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
* and all all values in a[0..9],b[0..9] must have magnitude less than
* INT32_MAX. */
static void
swap_conditional(limb a[19], limb b[19], limb iswap) {
unsigned i;
const s32 swap = (s32) -iswap;
for (i = 0; i < 10; ++i) {
const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) );
a[i] = ((s32)a[i]) ^ x;
b[i] = ((s32)b[i]) ^ x;
}
}
/* Calculates nQ where Q is the x-coordinate of a point on the curve
*
* resultx/resultz: the x coordinate of the resulting curve point (short form)
* n: a little endian, 32-byte number
* q: a point of the curve (short form) */
static void
cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
unsigned i, j;
memcpy(nqpqx, q, sizeof(limb) * 10);
for (i = 0; i < 32; ++i) {
u8 byte = n[31 - i];
for (j = 0; j < 8; ++j) {
const limb bit = byte >> 7;
swap_conditional(nqx, nqpqx, bit);
swap_conditional(nqz, nqpqz, bit);
fmonty(nqx2, nqz2,
nqpqx2, nqpqz2,
nqx, nqz,
nqpqx, nqpqz,
q);
swap_conditional(nqx2, nqpqx2, bit);
swap_conditional(nqz2, nqpqz2, bit);
t = nqx;
nqx = nqx2;
nqx2 = t;
t = nqz;
nqz = nqz2;
nqz2 = t;
t = nqpqx;
nqpqx = nqpqx2;
nqpqx2 = t;
t = nqpqz;
nqpqz = nqpqz2;
nqpqz2 = t;
byte <<= 1;
}
}
memcpy(resultx, nqx, sizeof(limb) * 10);
memcpy(resultz, nqz, sizeof(limb) * 10);
}
// -----------------------------------------------------------------------------
// Shamelessly copied from djb's code
// -----------------------------------------------------------------------------
static void
crecip(limb *out, const limb *z) {
limb z2[10];
limb z9[10];
limb z11[10];
limb z2_5_0[10];
limb z2_10_0[10];
limb z2_20_0[10];
limb z2_50_0[10];
limb z2_100_0[10];
limb t0[10];
limb t1[10];
int i;
/* 2 */ fsquare(z2,z);
/* 4 */ fsquare(t1,z2);
/* 8 */ fsquare(t0,t1);
/* 9 */ fmul(z9,t0,z);
/* 11 */ fmul(z11,z9,z2);
/* 22 */ fsquare(t0,z11);
/* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
/* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
/* 2^7 - 2^2 */ fsquare(t1,t0);
/* 2^8 - 2^3 */ fsquare(t0,t1);
/* 2^9 - 2^4 */ fsquare(t1,t0);
/* 2^10 - 2^5 */ fsquare(t0,t1);
/* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
/* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
/* 2^12 - 2^2 */ fsquare(t1,t0);
/* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
/* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
/* 2^22 - 2^2 */ fsquare(t1,t0);
/* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
/* 2^41 - 2^1 */ fsquare(t1,t0);
/* 2^42 - 2^2 */ fsquare(t0,t1);
/* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
/* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
/* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
/* 2^52 - 2^2 */ fsquare(t1,t0);
/* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
/* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
/* 2^102 - 2^2 */ fsquare(t0,t1);
/* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
/* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
/* 2^201 - 2^1 */ fsquare(t0,t1);
/* 2^202 - 2^2 */ fsquare(t1,t0);
/* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
/* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
/* 2^251 - 2^1 */ fsquare(t1,t0);
/* 2^252 - 2^2 */ fsquare(t0,t1);
/* 2^253 - 2^3 */ fsquare(t1,t0);
/* 2^254 - 2^4 */ fsquare(t0,t1);
/* 2^255 - 2^5 */ fsquare(t1,t0);
/* 2^255 - 21 */ fmul(out,t1,z11);
}
int
curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
limb bp[10], x[10], z[11], zmone[10];
uint8_t e[32];
int i;
for (i = 0; i < 32; ++i) e[i] = secret[i];
e[0] &= 248;
e[31] &= 127;
e[31] |= 64;
fexpand(bp, basepoint);
cmult(x, z, e, bp);
crecip(zmone, z);
fmul(z, x, zmone);
fcontract(mypublic, z);
return 0;
}

8
lib/curve25519/curve25519.h Executable file
View File

@@ -0,0 +1,8 @@
#ifndef CURVE25519_DONNA_H
#define CURVE25519_DONNA_H
static const unsigned char kCurve25519BasePoint[32] = { 9 };
int curve25519_donna(unsigned char *mypublic, const unsigned char *secret, const unsigned char *basepoint);
#endif

406
lib/dnssd.c Normal file
View File

@@ -0,0 +1,406 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
/* These defines allow us to compile on iOS */
#ifndef __has_feature
# define __has_feature(x) 0
#endif
#ifndef __has_extension
# define __has_extension __has_feature
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "dnssdint.h"
#include "dnssd.h"
#include "global.h"
#include "compat.h"
#include "utils.h"
#include <dns_sd.h>
#define MAX_DEVICEID 18
#define MAX_SERVNAME 256
#if defined(HAVE_LIBDL) && !defined(__APPLE__)
# define USE_LIBDL 1
#else
# define USE_LIBDL 0
#endif
#if defined(WIN32) || USE_LIBDL
# ifdef WIN32
# include <stdint.h>
# if !defined(EFI32) && !defined(EFI64)
# define DNSSD_STDCALL __stdcall
# else
# define DNSSD_STDCALL
# endif
# else
# include <dlfcn.h>
# define DNSSD_STDCALL
# endif
typedef struct _DNSServiceRef_t *DNSServiceRef;
typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
typedef uint32_t DNSServiceFlags;
typedef int32_t DNSServiceErrorType;
typedef void (DNSSD_STDCALL *DNSServiceRegisterReply)
(
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
void *context
);
#else
//# include <dns_sd.h>
# define DNSSD_STDCALL
#endif
typedef DNSServiceErrorType (DNSSD_STDCALL *DNSServiceRegister_t)
(
DNSServiceRef *sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
const char *name,
const char *regtype,
const char *domain,
const char *host,
uint16_t port,
uint16_t txtLen,
const void *txtRecord,
DNSServiceRegisterReply callBack,
void *context
);
typedef void (DNSSD_STDCALL *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef);
typedef void (DNSSD_STDCALL *TXTRecordCreate_t)
(
TXTRecordRef *txtRecord,
uint16_t bufferLen,
void *buffer
);
typedef void (DNSSD_STDCALL *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord);
typedef DNSServiceErrorType (DNSSD_STDCALL *TXTRecordSetValue_t)
(
TXTRecordRef *txtRecord,
const char *key,
uint8_t valueSize,
const void *value
);
typedef uint16_t (DNSSD_STDCALL *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord);
typedef const void * (DNSSD_STDCALL *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord);
struct dnssd_s {
#ifdef WIN32
HMODULE module;
#elif USE_LIBDL
void *module;
#endif
DNSServiceRegister_t DNSServiceRegister;
DNSServiceRefDeallocate_t DNSServiceRefDeallocate;
TXTRecordCreate_t TXTRecordCreate;
TXTRecordSetValue_t TXTRecordSetValue;
TXTRecordGetLength_t TXTRecordGetLength;
TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr;
TXTRecordDeallocate_t TXTRecordDeallocate;
TXTRecordRef raop_record;
TXTRecordRef airplay_record;
DNSServiceRef raop_service;
DNSServiceRef airplay_service;
char *name;
int name_len;
char *hw_addr;
int hw_addr_len;
};
dnssd_t *
dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error)
{
dnssd_t *dnssd;
if (error) *error = DNSSD_ERROR_NOERROR;
dnssd = calloc(1, sizeof(dnssd_t));
if (!dnssd) {
if (error) *error = DNSSD_ERROR_OUTOFMEM;
return NULL;
}
#ifdef WIN32
dnssd->module = LoadLibraryA("dnssd.dll");
if (!dnssd->module) {
if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
free(dnssd);
return NULL;
}
dnssd->DNSServiceRegister = (DNSServiceRegister_t)GetProcAddress(dnssd->module, "DNSServiceRegister");
dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)GetProcAddress(dnssd->module, "DNSServiceRefDeallocate");
dnssd->TXTRecordCreate = (TXTRecordCreate_t)GetProcAddress(dnssd->module, "TXTRecordCreate");
dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)GetProcAddress(dnssd->module, "TXTRecordSetValue");
dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)GetProcAddress(dnssd->module, "TXTRecordGetLength");
dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)GetProcAddress(dnssd->module, "TXTRecordGetBytesPtr");
dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)GetProcAddress(dnssd->module, "TXTRecordDeallocate");
if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
!dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
!dnssd->TXTRecordDeallocate) {
if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
FreeLibrary(dnssd->module);
free(dnssd);
return NULL;
}
#elif USE_LIBDL
dnssd->module = dlopen("libdns_sd.so", RTLD_LAZY);
if (!dnssd->module) {
if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
free(dnssd);
return NULL;
}
dnssd->DNSServiceRegister = (DNSServiceRegister_t)dlsym(dnssd->module, "DNSServiceRegister");
dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)dlsym(dnssd->module, "DNSServiceRefDeallocate");
dnssd->TXTRecordCreate = (TXTRecordCreate_t)dlsym(dnssd->module, "TXTRecordCreate");
dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)dlsym(dnssd->module, "TXTRecordSetValue");
dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)dlsym(dnssd->module, "TXTRecordGetLength");
dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)dlsym(dnssd->module, "TXTRecordGetBytesPtr");
dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)dlsym(dnssd->module, "TXTRecordDeallocate");
if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
!dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
!dnssd->TXTRecordDeallocate) {
if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
dlclose(dnssd->module);
free(dnssd);
return NULL;
}
#else
dnssd->DNSServiceRegister = &DNSServiceRegister;
dnssd->DNSServiceRefDeallocate = &DNSServiceRefDeallocate;
dnssd->TXTRecordCreate = &TXTRecordCreate;
dnssd->TXTRecordSetValue = &TXTRecordSetValue;
dnssd->TXTRecordGetLength = &TXTRecordGetLength;
dnssd->TXTRecordGetBytesPtr = &TXTRecordGetBytesPtr;
dnssd->TXTRecordDeallocate = &TXTRecordDeallocate;
#endif
dnssd->name_len = name_len;
dnssd->name = calloc(1, name_len);
if (!dnssd->name) {
free(dnssd);
if (error) *error = DNSSD_ERROR_OUTOFMEM;
return NULL;
}
memcpy(dnssd->name, name, name_len);
dnssd->hw_addr_len = hw_addr_len;
dnssd->hw_addr = calloc(1, dnssd->hw_addr_len);
if (!dnssd->hw_addr) {
free(dnssd->name);
free(dnssd);
if (error) *error = DNSSD_ERROR_OUTOFMEM;
return NULL;
}
memcpy(dnssd->hw_addr, hw_addr, hw_addr_len);
return dnssd;
}
void
dnssd_destroy(dnssd_t *dnssd)
{
if (dnssd) {
#ifdef WIN32
FreeLibrary(dnssd->module);
#elif USE_LIBDL
dlclose(dnssd->module);
#endif
free(dnssd);
}
}
int
dnssd_register_raop(dnssd_t *dnssd, unsigned short port)
{
char servname[MAX_SERVNAME];
assert(dnssd);
dnssd->TXTRecordCreate(&dnssd->raop_record, 0, NULL);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ch", strlen(RAOP_CH), RAOP_CH);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "cn", strlen(RAOP_CN), RAOP_CN);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "da", strlen(RAOP_DA), RAOP_DA);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "et", strlen(RAOP_ET), RAOP_ET);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "vv", strlen(RAOP_VV), RAOP_VV);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ft", strlen(RAOP_FT), RAOP_FT);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("false"), "false");
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sv", strlen(RAOP_SV), RAOP_SV);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "tp", strlen(RAOP_TP), RAOP_TP);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", strlen(RAOP_SF), RAOP_SF);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "vs", strlen(RAOP_VS), RAOP_VS);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "vn", strlen(RAOP_VN), RAOP_VN);
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pk", strlen(RAOP_PK), RAOP_PK);
/* Convert hardware address to string */
if (utils_hwaddr_raop(servname, sizeof(servname), dnssd->hw_addr, dnssd->hw_addr_len) < 0) {
/* FIXME: handle better */
return -1;
}
/* Check that we have bytes for 'hw@name' format */
if (sizeof(servname) < strlen(servname) + 1 + dnssd->name_len + 1) {
/* FIXME: handle better */
return -2;
}
strncat(servname, "@", sizeof(servname)-strlen(servname)-1);
strncat(servname, dnssd->name, sizeof(servname)-strlen(servname)-1);
/* Register the service */
dnssd->DNSServiceRegister(&dnssd->raop_service, 0, 0,
servname, "_raop._tcp",
NULL, NULL,
htons(port),
dnssd->TXTRecordGetLength(&dnssd->raop_record),
dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record),
NULL, NULL);
return 1;
}
int
dnssd_register_airplay(dnssd_t *dnssd, unsigned short port)
{
char device_id[3 * MAX_HWADDR_LEN];
assert(dnssd);
/* Convert hardware address to string */
if (utils_hwaddr_airplay(device_id, sizeof(device_id), dnssd->hw_addr, dnssd->hw_addr_len) < 0) {
/* FIXME: handle better */
return -1;
}
dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(AIRPLAY_FEATURES), AIRPLAY_FEATURES);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", strlen(AIRPLAY_FLAGS), AIRPLAY_FLAGS);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(AIRPLAY_PK), AIRPLAY_PK);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS);
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV);
/* Register the service */
dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0,
dnssd->name, "_airplay._tcp",
NULL, NULL,
htons(port),
dnssd->TXTRecordGetLength(&dnssd->airplay_record),
dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record),
NULL, NULL);
return 1;
}
const char *
dnssd_get_airplay_txt(dnssd_t *dnssd, int *length)
{
*length = dnssd->TXTRecordGetLength(&dnssd->airplay_record);
return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record);
}
const char *
dnssd_get_name(dnssd_t *dnssd, int *length)
{
*length = dnssd->name_len;
return dnssd->name;
}
const char *
dnssd_get_hw_addr(dnssd_t *dnssd, int *length)
{
*length = dnssd->hw_addr_len;
return dnssd->hw_addr;
}
void
dnssd_unregister_raop(dnssd_t *dnssd)
{
assert(dnssd);
if (!dnssd->raop_service) {
return;
}
/* Deallocate TXT record */
dnssd->TXTRecordDeallocate(&dnssd->raop_record);
dnssd->DNSServiceRefDeallocate(dnssd->raop_service);
dnssd->raop_service = NULL;
if (dnssd->airplay_service == NULL) {
free(dnssd->name);
free(dnssd->hw_addr);
}
}
void
dnssd_unregister_airplay(dnssd_t *dnssd)
{
assert(dnssd);
if (!dnssd->airplay_service) {
return;
}
/* Deallocate TXT record */
dnssd->TXTRecordDeallocate(&dnssd->airplay_record);
dnssd->DNSServiceRefDeallocate(dnssd->airplay_service);
dnssd->airplay_service = NULL;
if (dnssd->raop_service == NULL) {
free(dnssd->name);
free(dnssd->hw_addr);
}
}

39
lib/dnssd.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef DNSSD_H
#define DNSSD_H
#if defined(WIN32) && defined(DLL_EXPORT)
# define DNSSD_API __declspec(dllexport)
#else
# define DNSSD_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define DNSSD_ERROR_NOERROR 0
#define DNSSD_ERROR_HWADDRLEN 1
#define DNSSD_ERROR_OUTOFMEM 2
#define DNSSD_ERROR_LIBNOTFOUND 3
#define DNSSD_ERROR_PROCNOTFOUND 4
typedef struct dnssd_s dnssd_t;
DNSSD_API dnssd_t *dnssd_init(const char *name, int name_len, const char *hw_addr, int hw_addr_len, int *error);
DNSSD_API int dnssd_register_raop(dnssd_t *dnssd, unsigned short port);
DNSSD_API int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port);
DNSSD_API void dnssd_unregister_raop(dnssd_t *dnssd);
DNSSD_API void dnssd_unregister_airplay(dnssd_t *dnssd);
DNSSD_API const char *dnssd_get_airplay_txt(dnssd_t *dnssd, int *length);
DNSSD_API const char *dnssd_get_name(dnssd_t *dnssd, int *length);
DNSSD_API const char *dnssd_get_hw_addr(dnssd_t *dnssd, int *length);
DNSSD_API void dnssd_destroy(dnssd_t *dnssd);
#ifdef __cplusplus
}
#endif
#endif

29
lib/dnssdint.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef DNSSDINT_H
#define DNSSDINT_H
#define RAOP_TXTVERS "1"
#define RAOP_CH "2" /* Audio channels: 2 */
#define RAOP_CN "0,1,2,3" /* Audio codec: PCM, ALAC, AAC, AAC ELD */
#define RAOP_ET "0,3,5" /* Encryption type: None, FairPlay, FairPlay SAPv2.5 */
#define RAOP_VV "2"
#define RAOP_FT "0x5A7FFFF7,0x1E"
#define RAOP_RHD "5.6.0.0"
#define RAOP_SF "0x4"
#define RAOP_SV "false"
#define RAOP_DA "true"
#define RAOP_SR "44100" /* Sample rate: 44100 */
#define RAOP_SS "16" /* Sample size: 16 */
#define RAOP_VS "220.68"
#define RAOP_TP "UDP" /* Transport protocol. Possible values: UDP or TCP or TCP,UDP */
#define RAOP_MD "0,1,2" /* Metadata: text, artwork, progress */
#define RAOP_VN "65537"
#define RAOP_PK "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"
#define AIRPLAY_FEATURES "0x5A7FFFF7,0x1E"
#define AIRPLAY_SRCVERS "220.68"
#define AIRPLAY_FLAGS "0x4"
#define AIRPLAY_VV "2"
#define AIRPLAY_PK "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"
#define AIRPLAY_PI "2e388006-13ba-4041-9a67-25dd4a43d536"
#endif

6
lib/ed25519/CMakeLists.txt Executable file
View File

@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. ed25519_src)
set(DIR_SRCS ${ed25519_src})
add_library( ed25519
STATIC
${DIR_SRCS})

69
lib/ed25519/add_scalar.c Executable file
View File

@@ -0,0 +1,69 @@
#include "ed25519.h"
#include "ge.h"
#include "sc.h"
#include "sha512.h"
/* see http://crypto.stackexchange.com/a/6215/4697 */
void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) {
const unsigned char SC_1[32] = {1}; /* scalar with value 1 */
unsigned char n[32];
ge_p3 nB;
ge_p1p1 A_p1p1;
ge_p3 A;
ge_p3 public_key_unpacked;
ge_cached T;
sha512_context hash;
unsigned char hashbuf[64];
int i;
/* copy the scalar and clear highest bit */
for (i = 0; i < 31; ++i) {
n[i] = scalar[i];
}
n[31] = scalar[31] & 127;
/* private key: a = n + t */
if (private_key) {
sc_muladd(private_key, SC_1, n, private_key);
// https://github.com/orlp/ed25519/issues/3
sha512_init(&hash);
sha512_update(&hash, private_key + 32, 32);
sha512_update(&hash, scalar, 32);
sha512_final(&hash, hashbuf);
for (i = 0; i < 32; ++i) {
private_key[32 + i] = hashbuf[i];
}
}
/* public key: A = nB + T */
if (public_key) {
/* if we know the private key we don't need a point addition, which is faster */
/* using a "timing attack" you could find out wether or not we know the private
key, but this information seems rather useless - if this is important pass
public_key and private_key seperately in 2 function calls */
if (private_key) {
ge_scalarmult_base(&A, private_key);
} else {
/* unpack public key into T */
ge_frombytes_negate_vartime(&public_key_unpacked, public_key);
fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */
fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */
ge_p3_to_cached(&T, &public_key_unpacked);
/* calculate n*B */
ge_scalarmult_base(&nB, n);
/* A = n*B + T */
ge_add(&A_p1p1, &nB, &T);
ge_p1p1_to_p3(&A, &A_p1p1);
}
/* pack public key */
ge_p3_tobytes(public_key, &A);
}
}

38
lib/ed25519/ed25519.h Executable file
View File

@@ -0,0 +1,38 @@
#ifndef ED25519_H
#define ED25519_H
#include <stddef.h>
#if defined(_WIN32)
#if defined(ED25519_BUILD_DLL)
#define ED25519_DECLSPEC __declspec(dllexport)
#elif defined(ED25519_DLL)
#define ED25519_DECLSPEC __declspec(dllimport)
#else
#define ED25519_DECLSPEC
#endif
#else
#define ED25519_DECLSPEC
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ED25519_NO_SEED
int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed);
#endif
void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key);
void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
#ifdef __cplusplus
}
#endif
#endif

1491
lib/ed25519/fe.c Executable file

File diff suppressed because it is too large Load Diff

41
lib/ed25519/fe.h Executable file
View File

@@ -0,0 +1,41 @@
#ifndef FE_H
#define FE_H
#include "fixedint.h"
/*
fe means field element.
Here the field is \Z/(2^255-19).
An element t, entries t[0]...t[9], represents the integer
t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
Bounds on each t[i] vary depending on context.
*/
typedef int32_t fe[10];
void fe_0(fe h);
void fe_1(fe h);
void fe_frombytes(fe h, const unsigned char *s);
void fe_tobytes(unsigned char *s, const fe h);
void fe_copy(fe h, const fe f);
int fe_isnegative(const fe f);
int fe_isnonzero(const fe f);
void fe_cmov(fe f, const fe g, unsigned int b);
void fe_cswap(fe f, fe g, unsigned int b);
void fe_neg(fe h, const fe f);
void fe_add(fe h, const fe f, const fe g);
void fe_invert(fe out, const fe z);
void fe_sq(fe h, const fe f);
void fe_sq2(fe h, const fe f);
void fe_mul(fe h, const fe f, const fe g);
void fe_mul121666(fe h, fe f);
void fe_pow22523(fe out, const fe z);
void fe_sub(fe h, const fe f, const fe g);
#endif

72
lib/ed25519/fixedint.h Executable file
View File

@@ -0,0 +1,72 @@
/*
Portable header to provide the 32 and 64 bits type.
Not a compatible replacement for <stdint.h>, do not blindly use it as such.
*/
#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED)
#include <stdint.h>
#define FIXEDINT_H_INCLUDED
#if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C)
#include <limits.h>
#define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
#endif
#endif
#ifndef FIXEDINT_H_INCLUDED
#define FIXEDINT_H_INCLUDED
#include <limits.h>
/* (u)int32_t */
#ifndef uint32_t
#if (ULONG_MAX == 0xffffffffUL)
typedef unsigned long uint32_t;
#elif (UINT_MAX == 0xffffffffUL)
typedef unsigned int uint32_t;
#elif (USHRT_MAX == 0xffffffffUL)
typedef unsigned short uint32_t;
#endif
#endif
#ifndef int32_t
#if (LONG_MAX == 0x7fffffffL)
typedef signed long int32_t;
#elif (INT_MAX == 0x7fffffffL)
typedef signed int int32_t;
#elif (SHRT_MAX == 0x7fffffffL)
typedef signed short int32_t;
#endif
#endif
/* (u)int64_t */
#if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L)
typedef long long int64_t;
typedef unsigned long long uint64_t;
#define UINT64_C(v) v ##ULL
#define INT64_C(v) v ##LL
#elif defined(__GNUC__)
__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
#elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC)
typedef long long int64_t;
typedef unsigned long long uint64_t;
#define UINT64_C(v) v ##ULL
#define INT64_C(v) v ##LL
#elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC)
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define UINT64_C(v) v ##UI64
#define INT64_C(v) v ##I64
#endif
#endif

467
lib/ed25519/ge.c Executable file
View File

@@ -0,0 +1,467 @@
#include "ge.h"
#include "precomp_data.h"
/*
r = p + q
*/
void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->YplusX);
fe_mul(r->Y, r->Y, q->YminusX);
fe_mul(r->T, q->T2d, p->T);
fe_mul(r->X, p->Z, q->Z);
fe_add(t0, r->X, r->X);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_add(r->Z, t0, r->T);
fe_sub(r->T, t0, r->T);
}
static void slide(signed char *r, const unsigned char *a) {
int i;
int b;
int k;
for (i = 0; i < 256; ++i) {
r[i] = 1 & (a[i >> 3] >> (i & 7));
}
for (i = 0; i < 256; ++i)
if (r[i]) {
for (b = 1; b <= 6 && i + b < 256; ++b) {
if (r[i + b]) {
if (r[i] + (r[i + b] << b) <= 15) {
r[i] += r[i + b] << b;
r[i + b] = 0;
} else if (r[i] - (r[i + b] << b) >= -15) {
r[i] -= r[i + b] << b;
for (k = i + b; k < 256; ++k) {
if (!r[k]) {
r[k] = 1;
break;
}
r[k] = 0;
}
} else {
break;
}
}
}
}
}
/*
r = a * A + b * B
where a = a[0]+256*a[1]+...+256^31 a[31].
and b = b[0]+256*b[1]+...+256^31 b[31].
B is the Ed25519 base point (x,4/5) with x positive.
*/
void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
signed char aslide[256];
signed char bslide[256];
ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
ge_p1p1 t;
ge_p3 u;
ge_p3 A2;
int i;
slide(aslide, a);
slide(bslide, b);
ge_p3_to_cached(&Ai[0], A);
ge_p3_dbl(&t, A);
ge_p1p1_to_p3(&A2, &t);
ge_add(&t, &A2, &Ai[0]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[1], &u);
ge_add(&t, &A2, &Ai[1]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[2], &u);
ge_add(&t, &A2, &Ai[2]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[3], &u);
ge_add(&t, &A2, &Ai[3]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[4], &u);
ge_add(&t, &A2, &Ai[4]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[5], &u);
ge_add(&t, &A2, &Ai[5]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[6], &u);
ge_add(&t, &A2, &Ai[6]);
ge_p1p1_to_p3(&u, &t);
ge_p3_to_cached(&Ai[7], &u);
ge_p2_0(r);
for (i = 255; i >= 0; --i) {
if (aslide[i] || bslide[i]) {
break;
}
}
for (; i >= 0; --i) {
ge_p2_dbl(&t, r);
if (aslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_add(&t, &u, &Ai[aslide[i] / 2]);
} else if (aslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
}
if (bslide[i] > 0) {
ge_p1p1_to_p3(&u, &t);
ge_madd(&t, &u, &Bi[bslide[i] / 2]);
} else if (bslide[i] < 0) {
ge_p1p1_to_p3(&u, &t);
ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
}
ge_p1p1_to_p2(r, &t);
}
}
static const fe d = {
-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
};
static const fe sqrtm1 = {
-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
};
int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) {
fe u;
fe v;
fe v3;
fe vxx;
fe check;
fe_frombytes(h->Y, s);
fe_1(h->Z);
fe_sq(u, h->Y);
fe_mul(v, u, d);
fe_sub(u, u, h->Z); /* u = y^2-1 */
fe_add(v, v, h->Z); /* v = dy^2+1 */
fe_sq(v3, v);
fe_mul(v3, v3, v); /* v3 = v^3 */
fe_sq(h->X, v3);
fe_mul(h->X, h->X, v);
fe_mul(h->X, h->X, u); /* x = uv^7 */
fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */
fe_mul(h->X, h->X, v3);
fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */
fe_sq(vxx, h->X);
fe_mul(vxx, vxx, v);
fe_sub(check, vxx, u); /* vx^2-u */
if (fe_isnonzero(check)) {
fe_add(check, vxx, u); /* vx^2+u */
if (fe_isnonzero(check)) {
return -1;
}
fe_mul(h->X, h->X, sqrtm1);
}
if (fe_isnegative(h->X) == (s[31] >> 7)) {
fe_neg(h->X, h->X);
}
fe_mul(h->T, h->X, h->Y);
return 0;
}
/*
r = p + q
*/
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->yplusx);
fe_mul(r->Y, r->Y, q->yminusx);
fe_mul(r->T, q->xy2d, p->T);
fe_add(t0, p->Z, p->Z);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_add(r->Z, t0, r->T);
fe_sub(r->T, t0, r->T);
}
/*
r = p - q
*/
void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->yminusx);
fe_mul(r->Y, r->Y, q->yplusx);
fe_mul(r->T, q->xy2d, p->T);
fe_add(t0, p->Z, p->Z);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_sub(r->Z, t0, r->T);
fe_add(r->T, t0, r->T);
}
/*
r = p
*/
void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) {
fe_mul(r->X, p->X, p->T);
fe_mul(r->Y, p->Y, p->Z);
fe_mul(r->Z, p->Z, p->T);
}
/*
r = p
*/
void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) {
fe_mul(r->X, p->X, p->T);
fe_mul(r->Y, p->Y, p->Z);
fe_mul(r->Z, p->Z, p->T);
fe_mul(r->T, p->X, p->Y);
}
void ge_p2_0(ge_p2 *h) {
fe_0(h->X);
fe_1(h->Y);
fe_1(h->Z);
}
/*
r = 2 * p
*/
void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) {
fe t0;
fe_sq(r->X, p->X);
fe_sq(r->Z, p->Y);
fe_sq2(r->T, p->Z);
fe_add(r->Y, p->X, p->Y);
fe_sq(t0, r->Y);
fe_add(r->Y, r->Z, r->X);
fe_sub(r->Z, r->Z, r->X);
fe_sub(r->X, t0, r->Y);
fe_sub(r->T, r->T, r->Z);
}
void ge_p3_0(ge_p3 *h) {
fe_0(h->X);
fe_1(h->Y);
fe_1(h->Z);
fe_0(h->T);
}
/*
r = 2 * p
*/
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
ge_p2 q;
ge_p3_to_p2(&q, p);
ge_p2_dbl(r, &q);
}
/*
r = p
*/
static const fe d2 = {
-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199
};
void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) {
fe_add(r->YplusX, p->Y, p->X);
fe_sub(r->YminusX, p->Y, p->X);
fe_copy(r->Z, p->Z);
fe_mul(r->T2d, p->T, d2);
}
/*
r = p
*/
void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) {
fe_copy(r->X, p->X);
fe_copy(r->Y, p->Y);
fe_copy(r->Z, p->Z);
}
void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) {
fe recip;
fe x;
fe y;
fe_invert(recip, h->Z);
fe_mul(x, h->X, recip);
fe_mul(y, h->Y, recip);
fe_tobytes(s, y);
s[31] ^= fe_isnegative(x) << 7;
}
static unsigned char equal(signed char b, signed char c) {
unsigned char ub = b;
unsigned char uc = c;
unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
uint64_t y = x; /* 0: yes; 1..255: no */
y -= 1; /* large: yes; 0..254: no */
y >>= 63; /* 1: yes; 0: no */
return (unsigned char) y;
}
static unsigned char negative(signed char b) {
uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
x >>= 63; /* 1: yes; 0: no */
return (unsigned char) x;
}
static void cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) {
fe_cmov(t->yplusx, u->yplusx, b);
fe_cmov(t->yminusx, u->yminusx, b);
fe_cmov(t->xy2d, u->xy2d, b);
}
static void select(ge_precomp *t, int pos, signed char b) {
ge_precomp minust;
unsigned char bnegative = negative(b);
unsigned char babs = b - (((-bnegative) & b) << 1);
fe_1(t->yplusx);
fe_1(t->yminusx);
fe_0(t->xy2d);
cmov(t, &base[pos][0], equal(babs, 1));
cmov(t, &base[pos][1], equal(babs, 2));
cmov(t, &base[pos][2], equal(babs, 3));
cmov(t, &base[pos][3], equal(babs, 4));
cmov(t, &base[pos][4], equal(babs, 5));
cmov(t, &base[pos][5], equal(babs, 6));
cmov(t, &base[pos][6], equal(babs, 7));
cmov(t, &base[pos][7], equal(babs, 8));
fe_copy(minust.yplusx, t->yminusx);
fe_copy(minust.yminusx, t->yplusx);
fe_neg(minust.xy2d, t->xy2d);
cmov(t, &minust, bnegative);
}
/*
h = a * B
where a = a[0]+256*a[1]+...+256^31 a[31]
B is the Ed25519 base point (x,4/5) with x positive.
Preconditions:
a[31] <= 127
*/
void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
signed char e[64];
signed char carry;
ge_p1p1 r;
ge_p2 s;
ge_precomp t;
int i;
for (i = 0; i < 32; ++i) {
e[2 * i + 0] = (a[i] >> 0) & 15;
e[2 * i + 1] = (a[i] >> 4) & 15;
}
/* each e[i] is between 0 and 15 */
/* e[63] is between 0 and 7 */
carry = 0;
for (i = 0; i < 63; ++i) {
e[i] += carry;
carry = e[i] + 8;
carry >>= 4;
e[i] -= carry << 4;
}
e[63] += carry;
/* each e[i] is between -8 and 8 */
ge_p3_0(h);
for (i = 1; i < 64; i += 2) {
select(&t, i / 2, e[i]);
ge_madd(&r, h, &t);
ge_p1p1_to_p3(h, &r);
}
ge_p3_dbl(&r, h);
ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s);
ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s);
ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s);
ge_p1p1_to_p3(h, &r);
for (i = 0; i < 64; i += 2) {
select(&t, i / 2, e[i]);
ge_madd(&r, h, &t);
ge_p1p1_to_p3(h, &r);
}
}
/*
r = p - q
*/
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
fe_mul(r->Z, r->X, q->YminusX);
fe_mul(r->Y, r->Y, q->YplusX);
fe_mul(r->T, q->T2d, p->T);
fe_mul(r->X, p->Z, q->Z);
fe_add(t0, r->X, r->X);
fe_sub(r->X, r->Z, r->Y);
fe_add(r->Y, r->Z, r->Y);
fe_sub(r->Z, t0, r->T);
fe_add(r->T, t0, r->T);
}
void ge_tobytes(unsigned char *s, const ge_p2 *h) {
fe recip;
fe x;
fe y;
fe_invert(recip, h->Z);
fe_mul(x, h->X, recip);
fe_mul(y, h->Y, recip);
fe_tobytes(s, y);
s[31] ^= fe_isnegative(x) << 7;
}

74
lib/ed25519/ge.h Executable file
View File

@@ -0,0 +1,74 @@
#ifndef GE_H
#define GE_H
#include "fe.h"
/*
ge means group element.
Here the group is the set of pairs (x,y) of field elements (see fe.h)
satisfying -x^2 + y^2 = 1 + d x^2y^2
where d = -121665/121666.
Representations:
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
ge_precomp (Duif): (y+x,y-x,2dxy)
*/
typedef struct {
fe X;
fe Y;
fe Z;
} ge_p2;
typedef struct {
fe X;
fe Y;
fe Z;
fe T;
} ge_p3;
typedef struct {
fe X;
fe Y;
fe Z;
fe T;
} ge_p1p1;
typedef struct {
fe yplusx;
fe yminusx;
fe xy2d;
} ge_precomp;
typedef struct {
fe YplusX;
fe YminusX;
fe Z;
fe T2d;
} ge_cached;
void ge_p3_tobytes(unsigned char *s, const ge_p3 *h);
void ge_tobytes(unsigned char *s, const ge_p2 *h);
int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s);
void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b);
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
void ge_scalarmult_base(ge_p3 *h, const unsigned char *a);
void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p);
void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p);
void ge_p2_0(ge_p2 *h);
void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p);
void ge_p3_0(ge_p3 *h);
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p);
void ge_p3_to_cached(ge_cached *r, const ge_p3 *p);
void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p);
#endif

79
lib/ed25519/key_exchange.c Executable file
View File

@@ -0,0 +1,79 @@
#include "ed25519.h"
#include "fe.h"
void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) {
unsigned char e[32];
unsigned int i;
fe x1;
fe x2;
fe z2;
fe x3;
fe z3;
fe tmp0;
fe tmp1;
int pos;
unsigned int swap;
unsigned int b;
/* copy the private key and make sure it's valid */
for (i = 0; i < 32; ++i) {
e[i] = private_key[i];
}
e[0] &= 248;
e[31] &= 63;
e[31] |= 64;
/* unpack the public key and convert edwards to montgomery */
/* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */
fe_frombytes(x1, public_key);
fe_1(tmp1);
fe_add(tmp0, x1, tmp1);
fe_sub(tmp1, tmp1, x1);
fe_invert(tmp1, tmp1);
fe_mul(x1, tmp0, tmp1);
fe_1(x2);
fe_0(z2);
fe_copy(x3, x1);
fe_1(z3);
swap = 0;
for (pos = 254; pos >= 0; --pos) {
b = e[pos / 8] >> (pos & 7);
b &= 1;
swap ^= b;
fe_cswap(x2, x3, swap);
fe_cswap(z2, z3, swap);
swap = b;
/* from montgomery.h */
fe_sub(tmp0, x3, z3);
fe_sub(tmp1, x2, z2);
fe_add(x2, x2, z2);
fe_add(z2, x3, z3);
fe_mul(z3, tmp0, x2);
fe_mul(z2, z2, tmp1);
fe_sq(tmp0, tmp1);
fe_sq(tmp1, x2);
fe_add(x3, z3, z2);
fe_sub(z2, z3, z2);
fe_mul(x2, tmp1, tmp0);
fe_sub(tmp1, tmp1, tmp0);
fe_sq(z2, z2);
fe_mul121666(z3, tmp1);
fe_sq(x3, x3);
fe_add(tmp0, tmp0, z3);
fe_mul(z3, x1, z2);
fe_mul(z2, tmp1, tmp0);
}
fe_cswap(x2, x3, swap);
fe_cswap(z2, z3, swap);
fe_invert(z2, z2);
fe_mul(x2, x2, z2);
fe_tobytes(shared_secret, x2);
}

16
lib/ed25519/keypair.c Executable file
View File

@@ -0,0 +1,16 @@
#include "ed25519.h"
#include "sha512.h"
#include "ge.h"
void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) {
ge_p3 A;
sha512(seed, 32, private_key);
private_key[0] &= 248;
private_key[31] &= 63;
private_key[31] |= 64;
ge_scalarmult_base(&A, private_key);
ge_p3_tobytes(public_key, &A);
}

1391
lib/ed25519/precomp_data.h Executable file

File diff suppressed because it is too large Load Diff

809
lib/ed25519/sc.c Executable file
View File

@@ -0,0 +1,809 @@
#include "fixedint.h"
#include "sc.h"
static uint64_t load_3(const unsigned char *in) {
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
result |= ((uint64_t) in[2]) << 16;
return result;
}
static uint64_t load_4(const unsigned char *in) {
uint64_t result;
result = (uint64_t) in[0];
result |= ((uint64_t) in[1]) << 8;
result |= ((uint64_t) in[2]) << 16;
result |= ((uint64_t) in[3]) << 24;
return result;
}
/*
Input:
s[0]+256*s[1]+...+256^63*s[63] = s
Output:
s[0]+256*s[1]+...+256^31*s[31] = s mod l
where l = 2^252 + 27742317777372353535851937790883648493.
Overwrites s in place.
*/
void sc_reduce(unsigned char *s) {
int64_t s0 = 2097151 & load_3(s);
int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
int64_t s8 = 2097151 & load_3(s + 21);
int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
int64_t s11 = 2097151 & (load_4(s + 28) >> 7);
int64_t s12 = 2097151 & (load_4(s + 31) >> 4);
int64_t s13 = 2097151 & (load_3(s + 34) >> 1);
int64_t s14 = 2097151 & (load_4(s + 36) >> 6);
int64_t s15 = 2097151 & (load_3(s + 39) >> 3);
int64_t s16 = 2097151 & load_3(s + 42);
int64_t s17 = 2097151 & (load_4(s + 44) >> 5);
int64_t s18 = 2097151 & (load_3(s + 47) >> 2);
int64_t s19 = 2097151 & (load_4(s + 49) >> 7);
int64_t s20 = 2097151 & (load_4(s + 52) >> 4);
int64_t s21 = 2097151 & (load_3(s + 55) >> 1);
int64_t s22 = 2097151 & (load_4(s + 57) >> 6);
int64_t s23 = (load_4(s + 60) >> 3);
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
int64_t carry12;
int64_t carry13;
int64_t carry14;
int64_t carry15;
int64_t carry16;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s23 = 0;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s22 = 0;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s21 = 0;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s20 = 0;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s19 = 0;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
s18 = 0;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry12 = (s12 + (1 << 20)) >> 21;
s13 += carry12;
s12 -= carry12 << 21;
carry14 = (s14 + (1 << 20)) >> 21;
s15 += carry14;
s14 -= carry14 << 21;
carry16 = (s16 + (1 << 20)) >> 21;
s17 += carry16;
s16 -= carry16 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
carry13 = (s13 + (1 << 20)) >> 21;
s14 += carry13;
s13 -= carry13 << 21;
carry15 = (s15 + (1 << 20)) >> 21;
s16 += carry15;
s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s17 = 0;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s16 = 0;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s15 = 0;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s14 = 0;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s13 = 0;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1 << 20)) >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry2 = (s2 + (1 << 20)) >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry4 = (s4 + (1 << 20)) >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry1 = (s1 + (1 << 20)) >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry3 = (s3 + (1 << 20)) >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry5 = (s5 + (1 << 20)) >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry11 = s11 >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
s[0] = (unsigned char) (s0 >> 0);
s[1] = (unsigned char) (s0 >> 8);
s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
s[3] = (unsigned char) (s1 >> 3);
s[4] = (unsigned char) (s1 >> 11);
s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
s[6] = (unsigned char) (s2 >> 6);
s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
s[8] = (unsigned char) (s3 >> 1);
s[9] = (unsigned char) (s3 >> 9);
s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
s[11] = (unsigned char) (s4 >> 4);
s[12] = (unsigned char) (s4 >> 12);
s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
s[14] = (unsigned char) (s5 >> 7);
s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
s[16] = (unsigned char) (s6 >> 2);
s[17] = (unsigned char) (s6 >> 10);
s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
s[19] = (unsigned char) (s7 >> 5);
s[20] = (unsigned char) (s7 >> 13);
s[21] = (unsigned char) (s8 >> 0);
s[22] = (unsigned char) (s8 >> 8);
s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
s[24] = (unsigned char) (s9 >> 3);
s[25] = (unsigned char) (s9 >> 11);
s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
s[27] = (unsigned char) (s10 >> 6);
s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
s[29] = (unsigned char) (s11 >> 1);
s[30] = (unsigned char) (s11 >> 9);
s[31] = (unsigned char) (s11 >> 17);
}
/*
Input:
a[0]+256*a[1]+...+256^31*a[31] = a
b[0]+256*b[1]+...+256^31*b[31] = b
c[0]+256*c[1]+...+256^31*c[31] = c
Output:
s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
where l = 2^252 + 27742317777372353535851937790883648493.
*/
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) {
int64_t a0 = 2097151 & load_3(a);
int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
int64_t a8 = 2097151 & load_3(a + 21);
int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
int64_t a11 = (load_4(a + 28) >> 7);
int64_t b0 = 2097151 & load_3(b);
int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
int64_t b8 = 2097151 & load_3(b + 21);
int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
int64_t b11 = (load_4(b + 28) >> 7);
int64_t c0 = 2097151 & load_3(c);
int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
int64_t c8 = 2097151 & load_3(c + 21);
int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
int64_t c11 = (load_4(c + 28) >> 7);
int64_t s0;
int64_t s1;
int64_t s2;
int64_t s3;
int64_t s4;
int64_t s5;
int64_t s6;
int64_t s7;
int64_t s8;
int64_t s9;
int64_t s10;
int64_t s11;
int64_t s12;
int64_t s13;
int64_t s14;
int64_t s15;
int64_t s16;
int64_t s17;
int64_t s18;
int64_t s19;
int64_t s20;
int64_t s21;
int64_t s22;
int64_t s23;
int64_t carry0;
int64_t carry1;
int64_t carry2;
int64_t carry3;
int64_t carry4;
int64_t carry5;
int64_t carry6;
int64_t carry7;
int64_t carry8;
int64_t carry9;
int64_t carry10;
int64_t carry11;
int64_t carry12;
int64_t carry13;
int64_t carry14;
int64_t carry15;
int64_t carry16;
int64_t carry17;
int64_t carry18;
int64_t carry19;
int64_t carry20;
int64_t carry21;
int64_t carry22;
s0 = c0 + a0 * b0;
s1 = c1 + a0 * b1 + a1 * b0;
s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0;
s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0;
s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3;
s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4;
s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
s20 = a9 * b11 + a10 * b10 + a11 * b9;
s21 = a10 * b11 + a11 * b10;
s22 = a11 * b11;
s23 = 0;
carry0 = (s0 + (1 << 20)) >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry2 = (s2 + (1 << 20)) >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry4 = (s4 + (1 << 20)) >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry12 = (s12 + (1 << 20)) >> 21;
s13 += carry12;
s12 -= carry12 << 21;
carry14 = (s14 + (1 << 20)) >> 21;
s15 += carry14;
s14 -= carry14 << 21;
carry16 = (s16 + (1 << 20)) >> 21;
s17 += carry16;
s16 -= carry16 << 21;
carry18 = (s18 + (1 << 20)) >> 21;
s19 += carry18;
s18 -= carry18 << 21;
carry20 = (s20 + (1 << 20)) >> 21;
s21 += carry20;
s20 -= carry20 << 21;
carry22 = (s22 + (1 << 20)) >> 21;
s23 += carry22;
s22 -= carry22 << 21;
carry1 = (s1 + (1 << 20)) >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry3 = (s3 + (1 << 20)) >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry5 = (s5 + (1 << 20)) >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
carry13 = (s13 + (1 << 20)) >> 21;
s14 += carry13;
s13 -= carry13 << 21;
carry15 = (s15 + (1 << 20)) >> 21;
s16 += carry15;
s15 -= carry15 << 21;
carry17 = (s17 + (1 << 20)) >> 21;
s18 += carry17;
s17 -= carry17 << 21;
carry19 = (s19 + (1 << 20)) >> 21;
s20 += carry19;
s19 -= carry19 << 21;
carry21 = (s21 + (1 << 20)) >> 21;
s22 += carry21;
s21 -= carry21 << 21;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s23 = 0;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s22 = 0;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s21 = 0;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s20 = 0;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s19 = 0;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
s18 = 0;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry12 = (s12 + (1 << 20)) >> 21;
s13 += carry12;
s12 -= carry12 << 21;
carry14 = (s14 + (1 << 20)) >> 21;
s15 += carry14;
s14 -= carry14 << 21;
carry16 = (s16 + (1 << 20)) >> 21;
s17 += carry16;
s16 -= carry16 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
carry13 = (s13 + (1 << 20)) >> 21;
s14 += carry13;
s13 -= carry13 << 21;
carry15 = (s15 + (1 << 20)) >> 21;
s16 += carry15;
s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s17 = 0;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s16 = 0;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s15 = 0;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s14 = 0;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s13 = 0;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1 << 20)) >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry2 = (s2 + (1 << 20)) >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry4 = (s4 + (1 << 20)) >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry6 = (s6 + (1 << 20)) >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry8 = (s8 + (1 << 20)) >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry10 = (s10 + (1 << 20)) >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry1 = (s1 + (1 << 20)) >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry3 = (s3 + (1 << 20)) >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry5 = (s5 + (1 << 20)) >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry7 = (s7 + (1 << 20)) >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry9 = (s9 + (1 << 20)) >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry11 = (s11 + (1 << 20)) >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
carry11 = s11 >> 21;
s12 += carry11;
s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21;
s1 += carry0;
s0 -= carry0 << 21;
carry1 = s1 >> 21;
s2 += carry1;
s1 -= carry1 << 21;
carry2 = s2 >> 21;
s3 += carry2;
s2 -= carry2 << 21;
carry3 = s3 >> 21;
s4 += carry3;
s3 -= carry3 << 21;
carry4 = s4 >> 21;
s5 += carry4;
s4 -= carry4 << 21;
carry5 = s5 >> 21;
s6 += carry5;
s5 -= carry5 << 21;
carry6 = s6 >> 21;
s7 += carry6;
s6 -= carry6 << 21;
carry7 = s7 >> 21;
s8 += carry7;
s7 -= carry7 << 21;
carry8 = s8 >> 21;
s9 += carry8;
s8 -= carry8 << 21;
carry9 = s9 >> 21;
s10 += carry9;
s9 -= carry9 << 21;
carry10 = s10 >> 21;
s11 += carry10;
s10 -= carry10 << 21;
s[0] = (unsigned char) (s0 >> 0);
s[1] = (unsigned char) (s0 >> 8);
s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
s[3] = (unsigned char) (s1 >> 3);
s[4] = (unsigned char) (s1 >> 11);
s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
s[6] = (unsigned char) (s2 >> 6);
s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
s[8] = (unsigned char) (s3 >> 1);
s[9] = (unsigned char) (s3 >> 9);
s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
s[11] = (unsigned char) (s4 >> 4);
s[12] = (unsigned char) (s4 >> 12);
s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
s[14] = (unsigned char) (s5 >> 7);
s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
s[16] = (unsigned char) (s6 >> 2);
s[17] = (unsigned char) (s6 >> 10);
s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
s[19] = (unsigned char) (s7 >> 5);
s[20] = (unsigned char) (s7 >> 13);
s[21] = (unsigned char) (s8 >> 0);
s[22] = (unsigned char) (s8 >> 8);
s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
s[24] = (unsigned char) (s9 >> 3);
s[25] = (unsigned char) (s9 >> 11);
s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
s[27] = (unsigned char) (s10 >> 6);
s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
s[29] = (unsigned char) (s11 >> 1);
s[30] = (unsigned char) (s11 >> 9);
s[31] = (unsigned char) (s11 >> 17);
}

12
lib/ed25519/sc.h Executable file
View File

@@ -0,0 +1,12 @@
#ifndef SC_H
#define SC_H
/*
The set of scalars is \Z/l
where l = 2^252 + 27742317777372353535851937790883648493.
*/
void sc_reduce(unsigned char *s);
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c);
#endif

40
lib/ed25519/seed.c Executable file
View File

@@ -0,0 +1,40 @@
#include "ed25519.h"
#ifndef ED25519_NO_SEED
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#else
#include <stdio.h>
#endif
int ed25519_create_seed(unsigned char *seed) {
#ifdef _WIN32
HCRYPTPROV prov;
if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
return 1;
}
if (!CryptGenRandom(prov, 32, seed)) {
CryptReleaseContext(prov, 0);
return 1;
}
CryptReleaseContext(prov, 0);
#else
FILE *f = fopen("/dev/urandom", "rb");
if (f == NULL) {
return 1;
}
fread(seed, 1, 32, f);
fclose(f);
#endif
return 0;
}
#endif

275
lib/ed25519/sha512.c Executable file
View File

@@ -0,0 +1,275 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
*/
#include "fixedint.h"
#include "sha512.h"
/* the K array */
static const uint64_t K[80] = {
UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
};
/* Various logical functions */
#define ROR64c(x, y) \
( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \
((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF))
#define STORE64H(x, y) \
{ (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
(y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
(y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
(y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
#define LOAD64H(x, y) \
{ x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \
(((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \
(((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \
(((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); }
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
#define Maj(x,y,z) (((x | y) & z) | (x & y))
#define S(x, n) ROR64c(x, n)
#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n))
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
#ifndef MIN
#define MIN(x, y) ( ((x)<(y))?(x):(y) )
#endif
/* compress 1024-bits */
static int sha512_compress(sha512_context *md, unsigned char *buf)
{
uint64_t S[8], W[80], t0, t1;
int i;
/* copy state into S */
for (i = 0; i < 8; i++) {
S[i] = md->state[i];
}
/* copy the state into 1024-bits into W[0..15] */
for (i = 0; i < 16; i++) {
LOAD64H(W[i], buf + (8*i));
}
/* fill W[16..79] */
for (i = 16; i < 80; i++) {
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
}
/* Compress */
#define RND(a,b,c,d,e,f,g,h,i) \
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c);\
d += t0; \
h = t0 + t1;
for (i = 0; i < 80; i += 8) {
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
}
#undef RND
/* feedback */
for (i = 0; i < 8; i++) {
md->state[i] = md->state[i] + S[i];
}
return 0;
}
/**
Initialize the hash state
@param md The hash state you wish to initialize
@return 0 if successful
*/
int sha512_init(sha512_context * md) {
if (md == NULL) return 1;
md->curlen = 0;
md->length = 0;
md->state[0] = UINT64_C(0x6a09e667f3bcc908);
md->state[1] = UINT64_C(0xbb67ae8584caa73b);
md->state[2] = UINT64_C(0x3c6ef372fe94f82b);
md->state[3] = UINT64_C(0xa54ff53a5f1d36f1);
md->state[4] = UINT64_C(0x510e527fade682d1);
md->state[5] = UINT64_C(0x9b05688c2b3e6c1f);
md->state[6] = UINT64_C(0x1f83d9abfb41bd6b);
md->state[7] = UINT64_C(0x5be0cd19137e2179);
return 0;
}
/**
Process a block of memory though the hash
@param md The hash state
@param in The data to hash
@param inlen The length of the data (octets)
@return 0 if successful
*/
int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
{
size_t n;
size_t i;
int err;
if (md == NULL) return 1;
if (in == NULL) return 1;
if (md->curlen > sizeof(md->buf)) {
return 1;
}
while (inlen > 0) {
if (md->curlen == 0 && inlen >= 128) {
if ((err = sha512_compress (md, (unsigned char *)in)) != 0) {
return err;
}
md->length += 128 * 8;
in += 128;
inlen -= 128;
} else {
n = MIN(inlen, (128 - md->curlen));
for (i = 0; i < n; i++) {
md->buf[i + md->curlen] = in[i];
}
md->curlen += n;
in += n;
inlen -= n;
if (md->curlen == 128) {
if ((err = sha512_compress (md, md->buf)) != 0) {
return err;
}
md->length += 8*128;
md->curlen = 0;
}
}
}
return 0;
}
/**
Terminate the hash to get the digest
@param md The hash state
@param out [out] The destination of the hash (64 bytes)
@return 0 if successful
*/
int sha512_final(sha512_context * md, unsigned char *out)
{
int i;
if (md == NULL) return 1;
if (out == NULL) return 1;
if (md->curlen >= sizeof(md->buf)) {
return 1;
}
/* increase the length of the message */
md->length += md->curlen * UINT64_C(8);
/* append the '1' bit */
md->buf[md->curlen++] = (unsigned char)0x80;
/* if the length is currently above 112 bytes we append zeros
* then compress. Then we can fall back to padding zeros and length
* encoding like normal.
*/
if (md->curlen > 112) {
while (md->curlen < 128) {
md->buf[md->curlen++] = (unsigned char)0;
}
sha512_compress(md, md->buf);
md->curlen = 0;
}
/* pad upto 120 bytes of zeroes
* note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash
* > 2^64 bits of data... :-)
*/
while (md->curlen < 120) {
md->buf[md->curlen++] = (unsigned char)0;
}
/* store length */
STORE64H(md->length, md->buf+120);
sha512_compress(md, md->buf);
/* copy output */
for (i = 0; i < 8; i++) {
STORE64H(md->state[i], out+(8*i));
}
return 0;
}
int sha512(const unsigned char *message, size_t message_len, unsigned char *out)
{
sha512_context ctx;
int ret;
if ((ret = sha512_init(&ctx))) return ret;
if ((ret = sha512_update(&ctx, message, message_len))) return ret;
if ((ret = sha512_final(&ctx, out))) return ret;
return 0;
}

28
lib/ed25519/sha512.h Executable file
View File

@@ -0,0 +1,28 @@
#ifndef SHA512_H
#define SHA512_H
#include <stddef.h>
#include "fixedint.h"
/* state */
typedef struct sha512_context_ {
uint64_t length, state[8];
size_t curlen;
unsigned char buf[128];
} sha512_context;
#ifdef __cplusplus
extern "C" {
#endif
int sha512_init(sha512_context * md);
int sha512_final(sha512_context * md, unsigned char *out);
int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen);
int sha512(const unsigned char *message, size_t message_len, unsigned char *out);
#ifdef __cplusplus
}
#endif
#endif

31
lib/ed25519/sign.c Executable file
View File

@@ -0,0 +1,31 @@
#include "ed25519.h"
#include "sha512.h"
#include "ge.h"
#include "sc.h"
void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) {
sha512_context hash;
unsigned char hram[64];
unsigned char r[64];
ge_p3 R;
sha512_init(&hash);
sha512_update(&hash, private_key + 32, 32);
sha512_update(&hash, message, message_len);
sha512_final(&hash, r);
sc_reduce(r);
ge_scalarmult_base(&R, r);
ge_p3_tobytes(signature, &R);
sha512_init(&hash);
sha512_update(&hash, signature, 32);
sha512_update(&hash, public_key, 32);
sha512_update(&hash, message, message_len);
sha512_final(&hash, hram);
sc_reduce(hram);
sc_muladd(signature + 32, hram, private_key, r);
}

77
lib/ed25519/verify.c Executable file
View File

@@ -0,0 +1,77 @@
#include "ed25519.h"
#include "sha512.h"
#include "ge.h"
#include "sc.h"
static int consttime_equal(const unsigned char *x, const unsigned char *y) {
unsigned char r = 0;
r = x[0] ^ y[0];
#define F(i) r |= x[i] ^ y[i]
F(1);
F(2);
F(3);
F(4);
F(5);
F(6);
F(7);
F(8);
F(9);
F(10);
F(11);
F(12);
F(13);
F(14);
F(15);
F(16);
F(17);
F(18);
F(19);
F(20);
F(21);
F(22);
F(23);
F(24);
F(25);
F(26);
F(27);
F(28);
F(29);
F(30);
F(31);
#undef F
return !r;
}
int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) {
unsigned char h[64];
unsigned char checker[32];
sha512_context hash;
ge_p3 A;
ge_p2 R;
if (signature[63] & 224) {
return 0;
}
if (ge_frombytes_negate_vartime(&A, public_key) != 0) {
return 0;
}
sha512_init(&hash);
sha512_update(&hash, signature, 32);
sha512_update(&hash, public_key, 32);
sha512_update(&hash, message, message_len);
sha512_final(&hash, h);
sc_reduce(h);
ge_double_scalarmult_vartime(&R, h, &A, signature + 32);
ge_tobytes(checker, &R);
if (!consttime_equal(checker, signature)) {
return 0;
}
return 1;
}

0
lib/fairplay.h Normal file → Executable file
View File

87
lib/fairplay_playfair.c Executable file
View File

@@ -0,0 +1,87 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "fairplay.h"
#include "playfair/playfair.h"
char reply_message[4][142] = {{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x00,0x0f,0x9f,0x3f,0x9e,0x0a,0x25,0x21,0xdb,0xdf,0x31,0x2a,0xb2,0xbf,0xb2,0x9e,0x8d,0x23,0x2b,0x63,0x76,0xa8,0xc8,0x18,0x70,0x1d,0x22,0xae,0x93,0xd8,0x27,0x37,0xfe,0xaf,0x9d,0xb4,0xfd,0xf4,0x1c,0x2d,0xba,0x9d,0x1f,0x49,0xca,0xaa,0xbf,0x65,0x91,0xac,0x1f,0x7b,0xc6,0xf7,0xe0,0x66,0x3d,0x21,0xaf,0xe0,0x15,0x65,0x95,0x3e,0xab,0x81,0xf4,0x18,0xce,0xed,0x09,0x5a,0xdb,0x7c,0x3d,0x0e,0x25,0x49,0x09,0xa7,0x98,0x31,0xd4,0x9c,0x39,0x82,0x97,0x34,0x34,0xfa,0xcb,0x42,0xc6,0x3a,0x1c,0xd9,0x11,0xa6,0xfe,0x94,0x1a,0x8a,0x6d,0x4a,0x74,0x3b,0x46,0xc3,0xa7,0x64,0x9e,0x44,0xc7,0x89,0x55,0xe4,0x9d,0x81,0x55,0x00,0x95,0x49,0xc4,0xe2,0xf7,0xa3,0xf6,0xd5,0xba},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x01,0xcf,0x32,0xa2,0x57,0x14,0xb2,0x52,0x4f,0x8a,0xa0,0xad,0x7a,0xf1,0x64,0xe3,0x7b,0xcf,0x44,0x24,0xe2,0x00,0x04,0x7e,0xfc,0x0a,0xd6,0x7a,0xfc,0xd9,0x5d,0xed,0x1c,0x27,0x30,0xbb,0x59,0x1b,0x96,0x2e,0xd6,0x3a,0x9c,0x4d,0xed,0x88,0xba,0x8f,0xc7,0x8d,0xe6,0x4d,0x91,0xcc,0xfd,0x5c,0x7b,0x56,0xda,0x88,0xe3,0x1f,0x5c,0xce,0xaf,0xc7,0x43,0x19,0x95,0xa0,0x16,0x65,0xa5,0x4e,0x19,0x39,0xd2,0x5b,0x94,0xdb,0x64,0xb9,0xe4,0x5d,0x8d,0x06,0x3e,0x1e,0x6a,0xf0,0x7e,0x96,0x56,0x16,0x2b,0x0e,0xfa,0x40,0x42,0x75,0xea,0x5a,0x44,0xd9,0x59,0x1c,0x72,0x56,0xb9,0xfb,0xe6,0x51,0x38,0x98,0xb8,0x02,0x27,0x72,0x19,0x88,0x57,0x16,0x50,0x94,0x2a,0xd9,0x46,0x68,0x8a},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x02,0xc1,0x69,0xa3,0x52,0xee,0xed,0x35,0xb1,0x8c,0xdd,0x9c,0x58,0xd6,0x4f,0x16,0xc1,0x51,0x9a,0x89,0xeb,0x53,0x17,0xbd,0x0d,0x43,0x36,0xcd,0x68,0xf6,0x38,0xff,0x9d,0x01,0x6a,0x5b,0x52,0xb7,0xfa,0x92,0x16,0xb2,0xb6,0x54,0x82,0xc7,0x84,0x44,0x11,0x81,0x21,0xa2,0xc7,0xfe,0xd8,0x3d,0xb7,0x11,0x9e,0x91,0x82,0xaa,0xd7,0xd1,0x8c,0x70,0x63,0xe2,0xa4,0x57,0x55,0x59,0x10,0xaf,0x9e,0x0e,0xfc,0x76,0x34,0x7d,0x16,0x40,0x43,0x80,0x7f,0x58,0x1e,0xe4,0xfb,0xe4,0x2c,0xa9,0xde,0xdc,0x1b,0x5e,0xb2,0xa3,0xaa,0x3d,0x2e,0xcd,0x59,0xe7,0xee,0xe7,0x0b,0x36,0x29,0xf2,0x2a,0xfd,0x16,0x1d,0x87,0x73,0x53,0xdd,0xb9,0x9a,0xdc,0x8e,0x07,0x00,0x6e,0x56,0xf8,0x50,0xce},
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x03,0x90,0x01,0xe1,0x72,0x7e,0x0f,0x57,0xf9,0xf5,0x88,0x0d,0xb1,0x04,0xa6,0x25,0x7a,0x23,0xf5,0xcf,0xff,0x1a,0xbb,0xe1,0xe9,0x30,0x45,0x25,0x1a,0xfb,0x97,0xeb,0x9f,0xc0,0x01,0x1e,0xbe,0x0f,0x3a,0x81,0xdf,0x5b,0x69,0x1d,0x76,0xac,0xb2,0xf7,0xa5,0xc7,0x08,0xe3,0xd3,0x28,0xf5,0x6b,0xb3,0x9d,0xbd,0xe5,0xf2,0x9c,0x8a,0x17,0xf4,0x81,0x48,0x7e,0x3a,0xe8,0x63,0xc6,0x78,0x32,0x54,0x22,0xe6,0xf7,0x8e,0x16,0x6d,0x18,0xaa,0x7f,0xd6,0x36,0x25,0x8b,0xce,0x28,0x72,0x6f,0x66,0x1f,0x73,0x88,0x93,0xce,0x44,0x31,0x1e,0x4b,0xe6,0xc0,0x53,0x51,0x93,0xe5,0xef,0x72,0xe8,0x68,0x62,0x33,0x72,0x9c,0x22,0x7d,0x82,0x0c,0x99,0x94,0x45,0xd8,0x92,0x46,0xc8,0xc3,0x59}};
char fp_header[] = {0x46, 0x50, 0x4c, 0x59, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14};
struct fairplay_s {
logger_t *logger;
unsigned char keymsg[164];
unsigned int keymsglen;
};
fairplay_t *
fairplay_init(logger_t *logger)
{
fairplay_t *fp;
fp = calloc(1, sizeof(fairplay_t));
if (!fp) {
return NULL;
}
fp->logger = logger;
return fp;
}
int
fairplay_setup(fairplay_t *fp, const unsigned char req[16], unsigned char res[142])
{
int mode;
assert(fp);
if (req[4] != 0x03) {
/* Unsupported fairplay version */
return -1;
}
mode = req[14];
memcpy(res, reply_message[mode], 142);
fp->keymsglen = 0;
return 0;
}
int
fairplay_handshake(fairplay_t *fp, const unsigned char req[164], unsigned char res[32])
{
assert(fp);
if (req[4] != 0x03) {
/* Unsupported fairplay version */
return -1;
}
memcpy(fp->keymsg, req, 164);
fp->keymsglen = 164;
memcpy(res, fp_header, 12);
memcpy(res + 12, req + 144, 20);
return 0;
}
int
fairplay_decrypt(fairplay_t *fp, const unsigned char input[72], unsigned char output[16])
{
if (fp->keymsglen != 164) {
return -1;
}
playfair_decrypt(fp->keymsg, (unsigned char *) input, output);
return 0;
}
void
fairplay_destroy(fairplay_t *fp)
{
free(fp);
}

0
lib/global.h Normal file → Executable file
View File

2577
lib/http_parser.c Executable file

File diff suppressed because it is too large Load Diff

455
lib/http_parser.h Executable file
View File

@@ -0,0 +1,455 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* 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 http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 3
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 0
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
/* icecast */ \
XX(33, SOURCE, SOURCE) \
/* RFC-2326 (RTSP) */ \
XX(34, DESCRIBE, DESCRIBE) \
XX(35, ANNOUNCE, ANNOUNCE) \
XX(36, SETUP, SETUP) \
XX(37, PLAY, PLAY) \
XX(38, PAUSE, PAUSE) \
XX(39, TEARDOWN, TEARDOWN) \
XX(40, GET_PARAMETER, GET_PARAMETER) \
XX(41, SET_PARAMETER, SET_PARAMETER) \
XX(42, REDIRECT, REDIRECT) \
XX(43, RECORD, RECORD) \
/* RAOP */ \
XX(44, FLUSH, FLUSH) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
, F_TRANSFER_ENCODING = 1 << 8
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_TRANSFER_ENCODING, \
"request has invalid transfer-encoding") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
unsigned int flags : 16; /* F_* values from 'flags' enum; semi-public */
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Returns a string version of the HTTP status code. */
const char *http_status_str(enum http_status s);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
/* Change the maximum header size provided at compile time. */
void http_parser_set_max_header_size(uint32_t size);
#ifdef __cplusplus
}
#endif
#endif

252
lib/http_request.c Executable file
View File

@@ -0,0 +1,252 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "http_request.h"
#include "http_parser.h"
struct http_request_s {
http_parser parser;
http_parser_settings parser_settings;
const char *method;
char *url;
char **headers;
int headers_size;
int headers_index;
char *data;
int datalen;
int complete;
};
static int
on_url(http_parser *parser, const char *at, size_t length)
{
http_request_t *request = parser->data;
int urllen = request->url ? strlen(request->url) : 0;
request->url = realloc(request->url, urllen+length+1);
assert(request->url);
request->url[urllen] = '\0';
strncat(request->url, at, length);
return 0;
}
static int
on_header_field(http_parser *parser, const char *at, size_t length)
{
http_request_t *request = parser->data;
/* Check if our index is a value */
if (request->headers_index%2 == 1) {
request->headers_index++;
}
/* Allocate space for new field-value pair */
if (request->headers_index == request->headers_size) {
request->headers_size += 2;
request->headers = realloc(request->headers,
request->headers_size*sizeof(char*));
assert(request->headers);
request->headers[request->headers_index] = NULL;
request->headers[request->headers_index+1] = NULL;
}
/* Allocate space in the current header string */
if (request->headers[request->headers_index] == NULL) {
request->headers[request->headers_index] = calloc(1, length+1);
} else {
request->headers[request->headers_index] = realloc(
request->headers[request->headers_index],
strlen(request->headers[request->headers_index])+length+1
);
}
assert(request->headers[request->headers_index]);
strncat(request->headers[request->headers_index], at, length);
return 0;
}
static int
on_header_value(http_parser *parser, const char *at, size_t length)
{
http_request_t *request = parser->data;
/* Check if our index is a field */
if (request->headers_index%2 == 0) {
request->headers_index++;
}
/* Allocate space in the current header string */
if (request->headers[request->headers_index] == NULL) {
request->headers[request->headers_index] = calloc(1, length+1);
} else {
request->headers[request->headers_index] = realloc(
request->headers[request->headers_index],
strlen(request->headers[request->headers_index])+length+1
);
}
assert(request->headers[request->headers_index]);
strncat(request->headers[request->headers_index], at, length);
return 0;
}
static int
on_body(http_parser *parser, const char *at, size_t length)
{
http_request_t *request = parser->data;
request->data = realloc(request->data, request->datalen+length);
assert(request->data);
memcpy(request->data+request->datalen, at, length);
request->datalen += length;
return 0;
}
static int
on_message_complete(http_parser *parser)
{
http_request_t *request = parser->data;
request->method = http_method_str(request->parser.method);
request->complete = 1;
return 0;
}
http_request_t *
http_request_init(void)
{
http_request_t *request;
request = calloc(1, sizeof(http_request_t));
if (!request) {
return NULL;
}
http_parser_init(&request->parser, HTTP_REQUEST);
request->parser.data = request;
request->parser_settings.on_url = &on_url;
request->parser_settings.on_header_field = &on_header_field;
request->parser_settings.on_header_value = &on_header_value;
request->parser_settings.on_body = &on_body;
request->parser_settings.on_message_complete = &on_message_complete;
return request;
}
void
http_request_destroy(http_request_t *request)
{
int i;
if (request) {
free(request->url);
for (i=0; i<request->headers_size; i++) {
free(request->headers[i]);
}
free(request->headers);
free(request->data);
free(request);
}
}
int
http_request_add_data(http_request_t *request, const char *data, int datalen)
{
int ret;
assert(request);
ret = http_parser_execute(&request->parser,
&request->parser_settings,
data, datalen);
return ret;
}
int
http_request_is_complete(http_request_t *request)
{
assert(request);
return request->complete;
}
int
http_request_has_error(http_request_t *request)
{
assert(request);
return (HTTP_PARSER_ERRNO(&request->parser) != HPE_OK);
}
const char *
http_request_get_error_name(http_request_t *request)
{
assert(request);
return http_errno_name(HTTP_PARSER_ERRNO(&request->parser));
}
const char *
http_request_get_error_description(http_request_t *request)
{
assert(request);
return http_errno_description(HTTP_PARSER_ERRNO(&request->parser));
}
const char *
http_request_get_method(http_request_t *request)
{
assert(request);
return request->method;
}
const char *
http_request_get_url(http_request_t *request)
{
assert(request);
return request->url;
}
const char *
http_request_get_header(http_request_t *request, const char *name)
{
int i;
assert(request);
for (i=0; i<request->headers_size; i+=2) {
if (!strcmp(request->headers[i], name)) {
return request->headers[i+1];
}
}
return NULL;
}
const char *
http_request_get_data(http_request_t *request, int *datalen)
{
assert(request);
if (datalen) {
*datalen = request->datalen;
}
return request->data;
}

36
lib/http_request.h Executable file
View File

@@ -0,0 +1,36 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef HTTP_REQUEST_H
#define HTTP_REQUEST_H
typedef struct http_request_s http_request_t;
http_request_t *http_request_init(void);
int http_request_add_data(http_request_t *request, const char *data, int datalen);
int http_request_is_complete(http_request_t *request);
int http_request_has_error(http_request_t *request);
const char *http_request_get_error_name(http_request_t *request);
const char *http_request_get_error_description(http_request_t *request);
const char *http_request_get_method(http_request_t *request);
const char *http_request_get_url(http_request_t *request);
const char *http_request_get_header(http_request_t *request, const char *name);
const char *http_request_get_data(http_request_t *request, int *datalen);
void http_request_destroy(http_request_t *request);
#endif

165
lib/http_response.c Executable file
View File

@@ -0,0 +1,165 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "http_response.h"
#include "compat.h"
struct http_response_s {
int complete;
int disconnect;
char *data;
int data_size;
int data_length;
};
static void
http_response_add_data(http_response_t *response, const char *data, int datalen)
{
int newdatasize;
assert(response);
assert(data);
assert(datalen > 0);
newdatasize = response->data_size;
while (response->data_size+datalen > newdatasize) {
newdatasize *= 2;
}
if (newdatasize != response->data_size) {
response->data = realloc(response->data, newdatasize);
assert(response->data);
}
memcpy(response->data+response->data_length, data, datalen);
response->data_length += datalen;
}
http_response_t *
http_response_init(const char *protocol, int code, const char *message)
{
http_response_t *response;
char codestr[4];
assert(code >= 100 && code < 1000);
/* Convert code into string */
memset(codestr, 0, sizeof(codestr));
snprintf(codestr, sizeof(codestr), "%u", code);
response = calloc(1, sizeof(http_response_t));
if (!response) {
return NULL;
}
/* Allocate response data */
response->data_size = 1024;
response->data = malloc(response->data_size);
if (!response->data) {
free(response);
return NULL;
}
/* Add first line of response to the data array */
http_response_add_data(response, protocol, strlen(protocol));
http_response_add_data(response, " ", 1);
http_response_add_data(response, codestr, strlen(codestr));
http_response_add_data(response, " ", 1);
http_response_add_data(response, message, strlen(message));
http_response_add_data(response, "\r\n", 2);
return response;
}
void
http_response_destroy(http_response_t *response)
{
if (response) {
free(response->data);
free(response);
}
}
void
http_response_add_header(http_response_t *response, const char *name, const char *value)
{
assert(response);
assert(name);
assert(value);
http_response_add_data(response, name, strlen(name));
http_response_add_data(response, ": ", 2);
http_response_add_data(response, value, strlen(value));
http_response_add_data(response, "\r\n", 2);
}
void
http_response_finish(http_response_t *response, const char *data, int datalen)
{
assert(response);
assert(datalen==0 || (data && datalen > 0));
if (data && datalen > 0) {
const char *hdrname = "Content-Length";
char hdrvalue[16];
memset(hdrvalue, 0, sizeof(hdrvalue));
snprintf(hdrvalue, sizeof(hdrvalue)-1, "%d", datalen);
/* Add Content-Length header first */
http_response_add_data(response, hdrname, strlen(hdrname));
http_response_add_data(response, ": ", 2);
http_response_add_data(response, hdrvalue, strlen(hdrvalue));
http_response_add_data(response, "\r\n\r\n", 4);
/* Add data to the end of response */
http_response_add_data(response, data, datalen);
} else {
/* Add extra end of line after headers */
http_response_add_data(response, "\r\n", 2);
}
response->complete = 1;
}
void
http_response_set_disconnect(http_response_t *response, int disconnect)
{
assert(response);
response->disconnect = !!disconnect;
}
int
http_response_get_disconnect(http_response_t *response)
{
assert(response);
return response->disconnect;
}
const char *
http_response_get_data(http_response_t *response, int *datalen)
{
assert(response);
assert(datalen);
assert(response->complete);
*datalen = response->data_length;
return response->data;
}

32
lib/http_response.h Executable file
View File

@@ -0,0 +1,32 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef HTTP_RESPONSE_H
#define HTTP_RESPONSE_H
typedef struct http_response_s http_response_t;
http_response_t *http_response_init(const char *protocol, int code, const char *message);
void http_response_add_header(http_response_t *response, const char *name, const char *value);
void http_response_finish(http_response_t *response, const char *data, int datalen);
void http_response_set_disconnect(http_response_t *response, int disconnect);
int http_response_get_disconnect(http_response_t *response);
const char *http_response_get_data(http_response_t *response, int *datalen);
void http_response_destroy(http_response_t *response);
#endif

458
lib/httpd.c Executable file
View File

@@ -0,0 +1,458 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "httpd.h"
#include "netutils.h"
#include "http_request.h"
#include "compat.h"
#include "logger.h"
struct http_connection_s {
int connected;
int socket_fd;
void *user_data;
http_request_t *request;
};
typedef struct http_connection_s http_connection_t;
struct httpd_s {
logger_t *logger;
httpd_callbacks_t callbacks;
int max_connections;
int open_connections;
http_connection_t *connections;
/* These variables only edited mutex locked */
int running;
int joined;
thread_handle_t thread;
mutex_handle_t run_mutex;
/* Server fds for accepting connections */
int server_fd4;
int server_fd6;
};
httpd_t *
httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections)
{
httpd_t *httpd;
assert(logger);
assert(callbacks);
assert(max_connections > 0);
/* Allocate the httpd_t structure */
httpd = calloc(1, sizeof(httpd_t));
if (!httpd) {
return NULL;
}
httpd->max_connections = max_connections;
httpd->connections = calloc(max_connections, sizeof(http_connection_t));
if (!httpd->connections) {
free(httpd);
return NULL;
}
/* Use the logger provided */
httpd->logger = logger;
/* Save callback pointers */
memcpy(&httpd->callbacks, callbacks, sizeof(httpd_callbacks_t));
/* Initial status joined */
httpd->running = 0;
httpd->joined = 1;
return httpd;
}
void
httpd_destroy(httpd_t *httpd)
{
if (httpd) {
httpd_stop(httpd);
free(httpd->connections);
free(httpd);
}
}
static int
httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len, unsigned char *remote, int remote_len)
{
void *user_data;
int i;
for (i=0; i<httpd->max_connections; i++) {
if (!httpd->connections[i].connected) {
break;
}
}
if (i == httpd->max_connections) {
/* This code should never be reached, we do not select server_fds when full */
logger_log(httpd->logger, LOGGER_INFO, "Max connections reached");
return -1;
}
user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len);
if (!user_data) {
logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler");
return -1;
}
httpd->open_connections++;
httpd->connections[i].socket_fd = fd;
httpd->connections[i].connected = 1;
httpd->connections[i].user_data = user_data;
return 0;
}
static int
httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
{
struct sockaddr_storage remote_saddr;
socklen_t remote_saddrlen;
struct sockaddr_storage local_saddr;
socklen_t local_saddrlen;
unsigned char *local, *remote;
int local_len, remote_len;
int ret, fd;
remote_saddrlen = sizeof(remote_saddr);
fd = accept(server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen);
if (fd == -1) {
/* FIXME: Error happened */
return -1;
}
local_saddrlen = sizeof(local_saddr);
ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen);
if (ret == -1) {
shutdown(fd, SHUT_RDWR);
closesocket(fd);
return 0;
}
logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d",
(is_ipv6 ? "IPv6" : "IPv4"), fd);
local = netutils_get_address(&local_saddr, &local_len);
remote = netutils_get_address(&remote_saddr, &remote_len);
ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len);
if (ret == -1) {
shutdown(fd, SHUT_RDWR);
closesocket(fd);
return 0;
}
return 1;
}
static void
httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
{
if (connection->request) {
http_request_destroy(connection->request);
connection->request = NULL;
}
httpd->callbacks.conn_destroy(connection->user_data);
shutdown(connection->socket_fd, SHUT_WR);
closesocket(connection->socket_fd);
connection->connected = 0;
httpd->open_connections--;
}
static THREAD_RETVAL
httpd_thread(void *arg)
{
httpd_t *httpd = arg;
char buffer[1024];
int i;
assert(httpd);
while (1) {
fd_set rfds;
struct timeval tv;
int nfds=0;
int ret;
MUTEX_LOCK(httpd->run_mutex);
if (!httpd->running) {
MUTEX_UNLOCK(httpd->run_mutex);
break;
}
MUTEX_UNLOCK(httpd->run_mutex);
/* Set timeout value to 5ms */
tv.tv_sec = 1;
tv.tv_usec = 5000;
/* Get the correct nfds value and set rfds */
FD_ZERO(&rfds);
if (httpd->open_connections < httpd->max_connections) {
if (httpd->server_fd4 != -1) {
FD_SET(httpd->server_fd4, &rfds);
if (nfds <= httpd->server_fd4) {
nfds = httpd->server_fd4+1;
}
}
if (httpd->server_fd6 != -1) {
FD_SET(httpd->server_fd6, &rfds);
if (nfds <= httpd->server_fd6) {
nfds = httpd->server_fd6+1;
}
}
}
for (i=0; i<httpd->max_connections; i++) {
int socket_fd;
if (!httpd->connections[i].connected) {
continue;
}
socket_fd = httpd->connections[i].socket_fd;
FD_SET(socket_fd, &rfds);
if (nfds <= socket_fd) {
nfds = socket_fd+1;
}
}
ret = select(nfds, &rfds, NULL, NULL, &tv);
if (ret == 0) {
/* Timeout happened */
continue;
} else if (ret == -1) {
logger_log(httpd->logger, LOGGER_ERR, "httpd error in select");
break;
}
if (httpd->open_connections < httpd->max_connections &&
httpd->server_fd4 != -1 && FD_ISSET(httpd->server_fd4, &rfds)) {
ret = httpd_accept_connection(httpd, httpd->server_fd4, 0);
if (ret == -1) {
logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv4");
break;
} else if (ret == 0) {
continue;
}
}
if (httpd->open_connections < httpd->max_connections &&
httpd->server_fd6 != -1 && FD_ISSET(httpd->server_fd6, &rfds)) {
ret = httpd_accept_connection(httpd, httpd->server_fd6, 1);
if (ret == -1) {
logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv6");
break;
} else if (ret == 0) {
continue;
}
}
for (i=0; i<httpd->max_connections; i++) {
http_connection_t *connection = &httpd->connections[i];
if (!connection->connected) {
continue;
}
if (!FD_ISSET(connection->socket_fd, &rfds)) {
continue;
}
/* If not in the middle of request, allocate one */
if (!connection->request) {
connection->request = http_request_init();
assert(connection->request);
}
logger_log(httpd->logger, LOGGER_DEBUG, "httpd receiving on socket %d", connection->socket_fd);
ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0);
if (ret == 0) {
logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", connection->socket_fd);
httpd_remove_connection(httpd, connection);
continue;
}
/* Parse HTTP request from data read from connection */
http_request_add_data(connection->request, buffer, ret);
if (http_request_has_error(connection->request)) {
logger_log(httpd->logger, LOGGER_ERR, "httpd error in parsing: %s", http_request_get_error_name(connection->request));
httpd_remove_connection(httpd, connection);
continue;
}
/* If request is finished, process and deallocate */
if (http_request_is_complete(connection->request)) {
http_response_t *response = NULL;
// Callback the received data to raop
httpd->callbacks.conn_request(connection->user_data, connection->request, &response);
http_request_destroy(connection->request);
connection->request = NULL;
if (response) {
const char *data;
int datalen;
int written;
int ret;
/* Get response data and datalen */
data = http_response_get_data(response, &datalen);
written = 0;
while (written < datalen) {
ret = send(connection->socket_fd, data+written, datalen-written, 0);
if (ret == -1) {
logger_log(httpd->logger, LOGGER_ERR, "httpd error in sending data");
break;
}
written += ret;
}
if (http_response_get_disconnect(response)) {
logger_log(httpd->logger, LOGGER_INFO, "Disconnecting on software request");
httpd_remove_connection(httpd, connection);
}
} else {
logger_log(httpd->logger, LOGGER_WARNING, "httpd didn't get response");
}
http_response_destroy(response);
} else {
logger_log(httpd->logger, LOGGER_DEBUG, "Request not complete, waiting for more data...");
}
}
}
/* Remove all connections that are still connected */
for (i=0; i<httpd->max_connections; i++) {
http_connection_t *connection = &httpd->connections[i];
if (!connection->connected) {
continue;
}
logger_log(httpd->logger, LOGGER_INFO, "Removing connection for socket %d", connection->socket_fd);
httpd_remove_connection(httpd, connection);
}
/* Close server sockets since they are not used any more */
if (httpd->server_fd4 != -1) {
shutdown(httpd->server_fd4, SHUT_RDWR);
closesocket(httpd->server_fd4);
httpd->server_fd4 = -1;
}
if (httpd->server_fd6 != -1) {
shutdown(httpd->server_fd6, SHUT_RDWR);
closesocket(httpd->server_fd6);
httpd->server_fd6 = -1;
}
// Ensure running reflects the actual state
MUTEX_LOCK(httpd->run_mutex);
httpd->running = 0;
MUTEX_UNLOCK(httpd->run_mutex);
logger_log(httpd->logger, LOGGER_DEBUG, "Exiting HTTP thread");
return 0;
}
int
httpd_start(httpd_t *httpd, unsigned short *port)
{
/* How many connection attempts are kept in queue */
int backlog = 5;
assert(httpd);
assert(port);
MUTEX_LOCK(httpd->run_mutex);
if (httpd->running || !httpd->joined) {
MUTEX_UNLOCK(httpd->run_mutex);
return 0;
}
httpd->server_fd4 = netutils_init_socket(port, 0, 0);
if (httpd->server_fd4 == -1) {
logger_log(httpd->logger, LOGGER_ERR, "Error initialising socket %d", SOCKET_GET_ERROR());
MUTEX_UNLOCK(httpd->run_mutex);
return -1;
}
httpd->server_fd6 = -1;/*= netutils_init_socket(port, 1, 0);
if (httpd->server_fd6 == -1) {
logger_log(httpd->logger, LOGGER_WARNING, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR());
logger_log(httpd->logger, LOGGER_WARNING, "Continuing without IPv6 support");
}*/
if (httpd->server_fd4 != -1 && listen(httpd->server_fd4, backlog) == -1) {
logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket");
closesocket(httpd->server_fd4);
closesocket(httpd->server_fd6);
MUTEX_UNLOCK(httpd->run_mutex);
return -2;
}
if (httpd->server_fd6 != -1 && listen(httpd->server_fd6, backlog) == -1) {
logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv6 socket");
closesocket(httpd->server_fd4);
closesocket(httpd->server_fd6);
MUTEX_UNLOCK(httpd->run_mutex);
return -2;
}
logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)");
/* Set values correctly and create new thread */
httpd->running = 1;
httpd->joined = 0;
THREAD_CREATE(httpd->thread, httpd_thread, httpd);
MUTEX_UNLOCK(httpd->run_mutex);
return 1;
}
int
httpd_is_running(httpd_t *httpd)
{
int running;
assert(httpd);
MUTEX_LOCK(httpd->run_mutex);
running = httpd->running || !httpd->joined;
MUTEX_UNLOCK(httpd->run_mutex);
return running;
}
void
httpd_stop(httpd_t *httpd)
{
assert(httpd);
MUTEX_LOCK(httpd->run_mutex);
if (!httpd->running || httpd->joined) {
MUTEX_UNLOCK(httpd->run_mutex);
return;
}
httpd->running = 0;
MUTEX_UNLOCK(httpd->run_mutex);
THREAD_JOIN(httpd->thread);
MUTEX_LOCK(httpd->run_mutex);
httpd->joined = 1;
MUTEX_UNLOCK(httpd->run_mutex);
}

43
lib/httpd.h Executable file
View File

@@ -0,0 +1,43 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef HTTPD_H
#define HTTPD_H
#include "logger.h"
#include "http_request.h"
#include "http_response.h"
typedef struct httpd_s httpd_t;
struct httpd_callbacks_s {
void* opaque;
void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen);
void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response);
void (*conn_destroy)(void *ptr);
};
typedef struct httpd_callbacks_s httpd_callbacks_t;
httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections);
int httpd_is_running(httpd_t *httpd);
int httpd_start(httpd_t *httpd, unsigned short *port);
void httpd_stop(httpd_t *httpd);
void httpd_destroy(httpd_t *httpd);
#endif

139
lib/logger.c Executable file
View File

@@ -0,0 +1,139 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include "logger.h"
#include "compat.h"
struct logger_s {
mutex_handle_t lvl_mutex;
mutex_handle_t cb_mutex;
int level;
void *cls;
logger_callback_t callback;
};
logger_t *
logger_init()
{
logger_t *logger = calloc(1, sizeof(logger_t));
assert(logger);
MUTEX_CREATE(logger->lvl_mutex);
MUTEX_CREATE(logger->cb_mutex);
logger->level = LOGGER_WARNING;
logger->callback = NULL;
return logger;
}
void
logger_destroy(logger_t *logger)
{
MUTEX_DESTROY(logger->lvl_mutex);
MUTEX_DESTROY(logger->cb_mutex);
free(logger);
}
void
logger_set_level(logger_t *logger, int level)
{
assert(logger);
MUTEX_LOCK(logger->lvl_mutex);
logger->level = level;
MUTEX_UNLOCK(logger->lvl_mutex);
}
void
logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls)
{
assert(logger);
MUTEX_LOCK(logger->cb_mutex);
logger->cls = cls;
logger->callback = callback;
MUTEX_UNLOCK(logger->cb_mutex);
}
static char *
logger_utf8_to_local(const char *str)
{
char *ret = NULL;
/* FIXME: This is only implemented on Windows for now */
#if defined(_WIN32) || defined(_WIN64)
int wclen, mblen;
WCHAR *wcstr;
BOOL failed;
wclen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
wcstr = malloc(sizeof(WCHAR) * wclen);
MultiByteToWideChar(CP_UTF8, 0, str, -1, wcstr, wclen);
mblen = WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, NULL, 0, NULL, &failed);
if (failed) {
/* Invalid characters in input, conversion failed */
free(wcstr);
return NULL;
}
ret = malloc(sizeof(CHAR) * mblen);
WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, ret, mblen, NULL, NULL);
free(wcstr);
#endif
return ret;
}
void
logger_log(logger_t *logger, int level, const char *fmt, ...)
{
char buffer[4096];
va_list ap;
MUTEX_LOCK(logger->lvl_mutex);
if (level > logger->level) {
MUTEX_UNLOCK(logger->lvl_mutex);
return;
}
MUTEX_UNLOCK(logger->lvl_mutex);
buffer[sizeof(buffer)-1] = '\0';
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
va_end(ap);
MUTEX_LOCK(logger->cb_mutex);
if (logger->callback) {
logger->callback(logger->cls, level, buffer);
MUTEX_UNLOCK(logger->cb_mutex);
} else {
char *local;
MUTEX_UNLOCK(logger->cb_mutex);
local = logger_utf8_to_local(buffer);
if (local) {
fprintf(stderr, "%s\n", local);
free(local);
} else {
fprintf(stderr, "%s\n", buffer);
}
}
}

48
lib/logger.h Executable file
View File

@@ -0,0 +1,48 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef LOGGER_H
#define LOGGER_H
#ifdef __cplusplus
extern "C" {
#endif
/* Define syslog style log levels */
#define LOGGER_EMERG 0 /* system is unusable */
#define LOGGER_ALERT 1 /* action must be taken immediately */
#define LOGGER_CRIT 2 /* critical conditions */
#define LOGGER_ERR 3 /* error conditions */
#define LOGGER_WARNING 4 /* warning conditions */
#define LOGGER_NOTICE 5 /* normal but significant condition */
#define LOGGER_INFO 6 /* informational */
#define LOGGER_DEBUG 7 /* debug-level messages */
typedef void (*logger_callback_t)(void *cls, int level, const char *msg);
typedef struct logger_s logger_t;
logger_t *logger_init();
void logger_destroy(logger_t *logger);
void logger_set_level(logger_t *logger, int level);
void logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls);
void logger_log(logger_t *logger, int level, const char *fmt, ...);
#ifdef __cplusplus
}
#endif
#endif

53
lib/memalign.h Executable file
View File

@@ -0,0 +1,53 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef MEMALIGN_H
#define MEMALIGN_H
#if defined(WIN32)
#define SYSTEM_GET_PAGESIZE(ret) do {\
SYSTEM_INFO si;\
GetSystemInfo(&si);\
ret = si.dwPageSize;\
} while(0)
#define SYSTEM_GET_TIME(ret) ret = timeGetTime()
#define ALIGNED_MALLOC(memptr, alignment, size) do {\
char *ptr = malloc(sizeof(void*) + (size) + (alignment)-1);\
memptr = NULL;\
if (ptr) {\
size_t ptrval = (size_t)ptr + sizeof(void*) + (alignment)-1;\
ptrval = ptrval / (alignment) * (alignment);\
memptr = (void *)ptrval;\
*(((void **)memptr)-1) = ptr;\
}\
} while(0)
#define ALIGNED_FREE(memptr) free(*(((void **)memptr)-1))
#else
#define SYSTEM_GET_PAGESIZE(ret) ret = sysconf(_SC_PAGESIZE)
#define SYSTEM_GET_TIME(ret) do {\
struct timeval tv;\
gettimeofday(&tv, NULL);\
ret = (unsigned int)(tv.tv_sec*1000 + tv.tv_usec/1000);\
} while(0)
#define ALIGNED_MALLOC(memptr, alignment, size) if (posix_memalign((void **)&memptr, alignment, size)) memptr = NULL
#define ALIGNED_FREE(memptr) free(memptr)
#endif
#endif

142
lib/mirror_buffer.c Executable file
View File

@@ -0,0 +1,142 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include "mirror_buffer.h"
#include "raop_rtp.h"
#include "raop_rtp.h"
#include <stdint.h>
#include "crypto.h"
#include "compat.h"
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
//#define DUMP_KEI_IV
struct mirror_buffer_s {
logger_t *logger;
aes_ctx_t *aes_ctx;
int nextDecryptCount;
uint8_t og[16];
/* AES key and IV */
// Need secondary processing to use
unsigned char aeskey[RAOP_AESKEY_LEN];
unsigned char ecdh_secret[32];
};
void
mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnectionID)
{
sha_ctx_t *ctx = sha_init();
unsigned char eaeskey[64] = {};
memcpy(eaeskey, mirror_buffer->aeskey, 16);
sha_update(ctx, eaeskey, 16);
sha_update(ctx, mirror_buffer->ecdh_secret, 32);
sha_final(ctx, eaeskey, NULL);
unsigned char hash1[64];
unsigned char hash2[64];
char* skey = "AirPlayStreamKey";
char* siv = "AirPlayStreamIV";
unsigned char skeyall[255];
unsigned char sivall[255];
sprintf((char*) skeyall, "%s%llu", skey, streamConnectionID);
sprintf((char*) sivall, "%s%llu", siv, streamConnectionID);
sha_reset(ctx);
sha_update(ctx, skeyall, strlen((char*) skeyall));
sha_update(ctx, eaeskey, 16);
sha_final(ctx, hash1, NULL);
sha_reset(ctx);
sha_update(ctx, sivall, strlen((char*) sivall));
sha_update(ctx, eaeskey, 16);
sha_final(ctx, hash2, NULL);
sha_destroy(ctx);
unsigned char decrypt_aeskey[16];
unsigned char decrypt_aesiv[16];
memcpy(decrypt_aeskey, hash1, 16);
memcpy(decrypt_aesiv, hash2, 16);
#ifdef DUMP_KEI_IV
FILE* keyfile = fopen("/sdcard/111.keyiv", "wb");
fwrite(decrypt_aeskey, 16, 1, keyfile);
fwrite(decrypt_aesiv, 16, 1, keyfile);
fclose(keyfile);
#endif
// Need to be initialized externally
mirror_buffer->aes_ctx = aes_ctr_init(decrypt_aeskey, decrypt_aesiv);
mirror_buffer->nextDecryptCount = 0;
}
mirror_buffer_t *
mirror_buffer_init(logger_t *logger,
const unsigned char *aeskey,
const unsigned char *ecdh_secret)
{
mirror_buffer_t *mirror_buffer;
assert(aeskey);
assert(ecdh_secret);
mirror_buffer = calloc(1, sizeof(mirror_buffer_t));
if (!mirror_buffer) {
return NULL;
}
memcpy(mirror_buffer->aeskey, aeskey, RAOP_AESKEY_LEN);
memcpy(mirror_buffer->ecdh_secret, ecdh_secret, 32);
mirror_buffer->logger = logger;
mirror_buffer->nextDecryptCount = 0;
//mirror_buffer_init_aes(mirror_buffer, aeskey, ecdh_secret, streamConnectionID);
return mirror_buffer;
}
void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char* input, unsigned char* output, int inputLen) {
// Start decrypting
if (mirror_buffer->nextDecryptCount > 0) {//mirror_buffer->nextDecryptCount = 10
for (int i = 0; i < mirror_buffer->nextDecryptCount; i++) {
output[i] = (input[i] ^ mirror_buffer->og[(16 - mirror_buffer->nextDecryptCount) + i]);
}
}
// Handling encrypted bytes
int encryptlen = ((inputLen - mirror_buffer->nextDecryptCount) / 16) * 16;
// Aes decryption
aes_ctr_start_fresh_block(mirror_buffer->aes_ctx);
aes_ctr_decrypt(mirror_buffer->aes_ctx, input + mirror_buffer->nextDecryptCount,
input + mirror_buffer->nextDecryptCount, encryptlen);
// Copy to output
memcpy(output + mirror_buffer->nextDecryptCount, input + mirror_buffer->nextDecryptCount, encryptlen);
int outputlength = mirror_buffer->nextDecryptCount + encryptlen;
// Processing remaining length
int restlen = (inputLen - mirror_buffer->nextDecryptCount) % 16;
int reststart = inputLen - restlen;
mirror_buffer->nextDecryptCount = 0;
if (restlen > 0) {
memset(mirror_buffer->og, 0, 16);
memcpy(mirror_buffer->og, input + reststart, restlen);
aes_ctr_decrypt(mirror_buffer->aes_ctx, mirror_buffer->og, mirror_buffer->og, 16);
for (int j = 0; j < restlen; j++) {
output[reststart + j] = mirror_buffer->og[j];
}
outputlength += restlen;
mirror_buffer->nextDecryptCount = 16 - restlen;// Difference 16-6=10 bytes
}
}
void
mirror_buffer_destroy(mirror_buffer_t *mirror_buffer)
{
if (mirror_buffer) {
aes_ctr_destroy(mirror_buffer->aes_ctx);
free(mirror_buffer);
}
}

30
lib/mirror_buffer.h Executable file
View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef MIRROR_BUFFER_H
#define MIRROR_BUFFER_H
#include <stdint.h>
#include "logger.h"
typedef struct mirror_buffer_s mirror_buffer_t;
mirror_buffer_t *mirror_buffer_init( logger_t *logger,
const unsigned char *aeskey,
const unsigned char *ecdh_secret);
void mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnectionID);
void mirror_buffer_decrypt(mirror_buffer_t *raop_mirror, unsigned char* input, unsigned char* output, int datalen);
void mirror_buffer_destroy(mirror_buffer_t *mirror_buffer);
#endif //MIRROR_BUFFER_H

203
lib/netutils.c Executable file
View File

@@ -0,0 +1,203 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "compat.h"
int
netutils_init()
{
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
int ret;
wVersionRequested = MAKEWORD(2, 2);
ret = WSAStartup(wVersionRequested, &wsaData);
if (ret) {
return -1;
}
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2) {
/* Version mismatch, requested version not found */
return -1;
}
#endif
return 0;
}
void
netutils_cleanup()
{
#ifdef WIN32
WSACleanup();
#endif
}
unsigned char *
netutils_get_address(void *sockaddr, int *length)
{
unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
struct sockaddr *address = sockaddr;
assert(address);
assert(length);
if (address->sa_family == AF_INET) {
struct sockaddr_in *sin;
sin = (struct sockaddr_in *)address;
*length = sizeof(sin->sin_addr.s_addr);
return (unsigned char *)&sin->sin_addr.s_addr;
} else if (address->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)address;
if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
/* Actually an embedded IPv4 address */
*length = sizeof(sin6->sin6_addr.s6_addr)-12;
return (sin6->sin6_addr.s6_addr+12);
}
*length = sizeof(sin6->sin6_addr.s6_addr);
return sin6->sin6_addr.s6_addr;
}
*length = 0;
return NULL;
}
int
netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
{
int family = use_ipv6 ? AF_INET6 : AF_INET;
int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
struct sockaddr_storage saddr;
socklen_t socklen;
int server_fd;
int ret;
int reuseaddr = 1;
assert(port);
server_fd = socket(family, type, proto);
if (server_fd == -1) {
goto cleanup;
}
ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof (reuseaddr));
if (ret == -1) {
goto cleanup;
}
memset(&saddr, 0, sizeof(saddr));
if (use_ipv6) {
struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
int v6only = 1;
/* Initialize sockaddr for bind */
sin6ptr->sin6_family = family;
sin6ptr->sin6_addr = in6addr_any;
sin6ptr->sin6_port = htons(*port);
#ifndef WIN32
/* Make sure we only listen to IPv6 addresses */
setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
(char *) &v6only, sizeof(v6only));
#endif
socklen = sizeof(*sin6ptr);
ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
if (ret == -1) {
goto cleanup;
}
ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
if (ret == -1) {
goto cleanup;
}
*port = ntohs(sin6ptr->sin6_port);
} else {
struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
/* Initialize sockaddr for bind */
sinptr->sin_family = family;
sinptr->sin_addr.s_addr = INADDR_ANY;
sinptr->sin_port = htons(*port);
socklen = sizeof(*sinptr);
ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
if (ret == -1) {
goto cleanup;
}
ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
if (ret == -1) {
goto cleanup;
}
*port = ntohs(sinptr->sin_port);
}
return server_fd;
cleanup:
ret = SOCKET_GET_ERROR();
if (server_fd != -1) {
closesocket(server_fd);
}
SOCKET_SET_ERROR(ret);
return -1;
}
// Src is the ip address
int
netutils_parse_address(int family, const char *src, void *dst, int dstlen)
{
struct addrinfo *result;
struct addrinfo *ptr;
struct addrinfo hints;
int length;
int ret;
if (family != AF_INET && family != AF_INET6) {
return -1;
}
if (!src || !dst) {
return -1;
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
ret = getaddrinfo(src, NULL, &hints, &result);
if (ret != 0) {
return -1;
}
length = -1;
for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
length = ptr->ai_addrlen;
break;
}
}
freeaddrinfo(result);
return length;
}

0
lib/netutils.h Normal file → Executable file
View File

261
lib/pairing.c Executable file
View File

@@ -0,0 +1,261 @@
/**
* Copyright (C) 2018 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "pairing.h"
#include "curve25519/curve25519.h"
#include "ed25519/ed25519.h"
#include "crypto.h"
#define SALT_KEY "Pair-Verify-AES-Key"
#define SALT_IV "Pair-Verify-AES-IV"
struct pairing_s {
unsigned char ed_private[64];
unsigned char ed_public[32];
};
typedef enum {
STATUS_INITIAL,
STATUS_SETUP,
STATUS_HANDSHAKE,
STATUS_FINISHED
} status_t;
struct pairing_session_s {
status_t status;
unsigned char ed_private[64];
unsigned char ed_ours[32];
unsigned char ed_theirs[32];
unsigned char ecdh_ours[32];
unsigned char ecdh_theirs[32];
unsigned char ecdh_secret[32];
};
static int
derive_key_internal(pairing_session_t *session, const unsigned char *salt, unsigned int saltlen, unsigned char *key, unsigned int keylen)
{
unsigned char hash[64];
if (keylen > sizeof(hash)) {
return -1;
}
sha_ctx_t *ctx = sha_init();
sha_update(ctx, salt, saltlen);
sha_update(ctx, session->ecdh_secret, 32);
sha_final(ctx, hash, NULL);
sha_destroy(ctx);
memcpy(key, hash, keylen);
return 0;
}
pairing_t *
pairing_init_generate()
{
unsigned char seed[32];
if (ed25519_create_seed(seed)) {
return NULL;
}
return pairing_init_seed(seed);
}
pairing_t *
pairing_init_seed(const unsigned char seed[32])
{
pairing_t *pairing;
pairing = calloc(1, sizeof(pairing_t));
if (!pairing) {
return NULL;
}
ed25519_create_keypair(pairing->ed_public, pairing->ed_private, seed);
return pairing;
}
void
pairing_get_public_key(pairing_t *pairing, unsigned char public_key[32])
{
assert(pairing);
memcpy(public_key, pairing->ed_public, 32);
}
void
pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[32])
{
assert(session);
memcpy(ecdh_secret, session->ecdh_secret, 32);
}
pairing_session_t *
pairing_session_init(pairing_t *pairing)
{
pairing_session_t *session;
if (!pairing) {
return NULL;
}
session = calloc(1, sizeof(pairing_session_t));
if (!session) {
return NULL;
}
memcpy(session->ed_private, pairing->ed_private, 64);
memcpy(session->ed_ours, pairing->ed_public, 32);
session->status = STATUS_INITIAL;
return session;
}
void
pairing_session_set_setup_status(pairing_session_t *session)
{
assert(session);
session->status = STATUS_SETUP;
}
int
pairing_session_check_handshake_status(pairing_session_t *session)
{
assert(session);
if (session->status != STATUS_SETUP) {
return -1;
}
return 0;
}
int
pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[32], const unsigned char ed_key[32])
{
unsigned char ecdh_priv[32];
assert(session);
if (session->status == STATUS_FINISHED) {
return -1;
}
if (ed25519_create_seed(ecdh_priv)) {
return -2;
}
memcpy(session->ecdh_theirs, ecdh_key, 32);
memcpy(session->ed_theirs, ed_key, 32);
curve25519_donna(session->ecdh_ours, ecdh_priv, kCurve25519BasePoint);
curve25519_donna(session->ecdh_secret, ecdh_priv, session->ecdh_theirs);
session->status = STATUS_HANDSHAKE;
return 0;
}
int
pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[32])
{
assert(session);
if (session->status != STATUS_HANDSHAKE) {
return -1;
}
memcpy(ecdh_key, session->ecdh_ours, 32);
return 0;
}
int
pairing_session_get_signature(pairing_session_t *session, unsigned char signature[64])
{
unsigned char sig_msg[64];
unsigned char key[16];
unsigned char iv[16];
aes_ctx_t *aes_ctx;
assert(session);
if (session->status != STATUS_HANDSHAKE) {
return -1;
}
/* First sign the public ECDH keys of both parties */
memcpy(&sig_msg[0], session->ecdh_ours, 32);
memcpy(&sig_msg[32], session->ecdh_theirs, 32);
ed25519_sign(signature, sig_msg, sizeof(sig_msg), session->ed_ours, session->ed_private);
/* Then encrypt the result with keys derived from the shared secret */
derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key));
derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(key));
aes_ctx = aes_ctr_init(key, iv);
aes_ctr_encrypt(aes_ctx, signature, signature, 64);
aes_ctr_destroy(aes_ctx);
return 0;
}
int
pairing_session_finish(pairing_session_t *session, const unsigned char signature[64])
{
unsigned char sig_buffer[64];
unsigned char sig_msg[64];
unsigned char key[16];
unsigned char iv[16];
aes_ctx_t *aes_ctx;
assert(session);
if (session->status != STATUS_HANDSHAKE) {
return -1;
}
/* First decrypt the signature with keys derived from the shared secret */
derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key));
derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(key));
aes_ctx = aes_ctr_init(key, iv);
/* One fake round for the initial handshake encryption */
aes_ctr_encrypt(aes_ctx, sig_buffer, sig_buffer, 64);
aes_ctr_encrypt(aes_ctx, signature, sig_buffer, 64);
aes_ctr_destroy(aes_ctx);
/* Then verify the signature with public ECDH keys of both parties */
memcpy(&sig_msg[0], session->ecdh_theirs, 32);
memcpy(&sig_msg[32], session->ecdh_ours, 32);
if (!ed25519_verify(sig_buffer, sig_msg, sizeof(sig_msg), session->ed_theirs)) {
return -2;
}
session->status = STATUS_FINISHED;
return 0;
}
void
pairing_session_destroy(pairing_session_t *session)
{
free(session);
}
void
pairing_destroy(pairing_t *pairing)
{
free(pairing);
}

38
lib/pairing.h Executable file
View File

@@ -0,0 +1,38 @@
/**
* Copyright (C) 2018 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef PAIRING_H
#define PAIRING_H
typedef struct pairing_s pairing_t;
typedef struct pairing_session_s pairing_session_t;
pairing_t *pairing_init_generate();
pairing_t *pairing_init_seed(const unsigned char seed[32]);
void pairing_get_public_key(pairing_t *pairing, unsigned char public_key[32]);
pairing_session_t *pairing_session_init(pairing_t *pairing);
void pairing_session_set_setup_status(pairing_session_t *session);
int pairing_session_check_handshake_status(pairing_session_t *session);
int pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[32], const unsigned char ed_key[32]);
int pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[32]);
int pairing_session_get_signature(pairing_session_t *session, unsigned char signature[64]);
int pairing_session_finish(pairing_session_t *session, const unsigned char signature[64]);
void pairing_session_destroy(pairing_session_t *session);
void pairing_destroy(pairing_t *pairing);
void pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[32]);
#endif

7
lib/playfair/CMakeLists.txt Executable file
View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. playfair_src)
set(DIR_SRCS ${playfair_src})
include_directories(.)
add_library( playfair
STATIC
${DIR_SRCS})

637
lib/playfair/LICENSE.md Executable file
View File

@@ -0,0 +1,637 @@
# GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/)
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
## Preamble
The GNU General Public License is a free, copyleft license for software and
other kinds of works.
The licenses for most software and other practical works are designed to take
away your freedom to share and change the works. By contrast, the GNU General
Public License is intended to guarantee your freedom to share and change all
versions of a program--to make sure it remains free software for all its users.
We, the Free Software Foundation, use the GNU General Public License for most
of our software; it applies also to any other work released this way by its
authors. You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the freedom to
distribute copies of free software (and charge for them if you wish), that you
receive source code or can get it if you want it, that you can change the
software or use pieces of it in new free programs, and that you know you can do
these things.
To protect your rights, we need to prevent others from denying you these rights
or asking you to surrender the rights. Therefore, you have certain
responsibilities if you distribute copies of the software, or if you modify it:
responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether gratis or for
a fee, you must pass on to the recipients the same freedoms that you received.
You must make sure that they, too, receive or can get the source code. And you
must show them these terms so they know their rights.
Developers that use the GNU GPL protect your rights with two steps:
1. assert copyright on the software, and
2. offer you this License giving you legal permission to copy, distribute
and/or modify it.
For the developers' and authors' protection, the GPL clearly explains that
there is no warranty for this free software. For both users' and authors' sake,
the GPL requires that modified versions be marked as changed, so that their
problems will not be attributed erroneously to authors of previous versions.
Some devices are designed to deny users access to install or run modified
versions of the software inside them, although the manufacturer can do so. This
is fundamentally incompatible with the aim of protecting users' freedom to
change the software. The systematic pattern of such abuse occurs in the area of
products for individuals to use, which is precisely where it is most
unacceptable. Therefore, we have designed this version of the GPL to prohibit
the practice for those products. If such problems arise substantially in other
domains, we stand ready to extend this provision to those domains in future
versions of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents. States
should not allow patents to restrict development and use of software on
general-purpose computers, but in those that do, we wish to avoid the special
danger that patents applied to a free program could make it effectively
proprietary. To prevent this, the GPL assures that patents cannot be used to
render the program non-free.
The precise terms and conditions for copying, distribution and modification
follow.
## TERMS AND CONDITIONS
### 0. Definitions.
*This License* refers to version 3 of the GNU General Public License.
*Copyright* also means copyright-like laws that apply to other kinds of works,
such as semiconductor masks.
*The Program* refers to any copyrightable work licensed under this License.
Each licensee is addressed as *you*. *Licensees* and *recipients* may be
individuals or organizations.
To *modify* a work means to copy from or adapt all or part of the work in a
fashion requiring copyright permission, other than the making of an exact copy.
The resulting work is called a *modified version* of the earlier work or a work
*based on* the earlier work.
A *covered work* means either the unmodified Program or a work based on the
Program.
To *propagate* a work means to do anything with it that, without permission,
would make you directly or secondarily liable for infringement under applicable
copyright law, except executing it on a computer or modifying a private copy.
Propagation includes copying, distribution (with or without modification),
making available to the public, and in some countries other activities as well.
To *convey* a work means any kind of propagation that enables other parties to
make or receive copies. Mere interaction with a user through a computer
network, with no transfer of a copy, is not conveying.
An interactive user interface displays *Appropriate Legal Notices* to the
extent that it includes a convenient and prominently visible feature that
1. displays an appropriate copyright notice, and
2. tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the work
under this License, and how to view a copy of this License.
If the interface presents a list of user commands or options, such as a menu, a
prominent item in the list meets this criterion.
### 1. Source Code.
The *source code* for a work means the preferred form of the work for making
modifications to it. *Object code* means any non-source form of a work.
A *Standard Interface* means an interface that either is an official standard
defined by a recognized standards body, or, in the case of interfaces specified
for a particular programming language, one that is widely used among developers
working in that language.
The *System Libraries* of an executable work include anything, other than the
work as a whole, that (a) is included in the normal form of packaging a Major
Component, but which is not part of that Major Component, and (b) serves only
to enable use of the work with that Major Component, or to implement a Standard
Interface for which an implementation is available to the public in source code
form. A *Major Component*, in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system (if any) on
which the executable work runs, or a compiler used to produce the work, or an
object code interpreter used to run it.
The *Corresponding Source* for a work in object code form means all the source
code needed to generate, install, and (for an executable work) run the object
code and to modify the work, including scripts to control those activities.
However, it does not include the work's System Libraries, or general-purpose
tools or generally available free programs which are used unmodified in
performing those activities but which are not part of the work. For example,
Corresponding Source includes interface definition files associated with source
files for the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require, such as
by intimate data communication or control flow between those subprograms and
other parts of the work.
The Corresponding Source need not include anything that users can regenerate
automatically from other parts of the Corresponding Source.
The Corresponding Source for a work in source code form is that same work.
### 2. Basic Permissions.
All rights granted under this License are granted for the term of copyright on
the Program, and are irrevocable provided the stated conditions are met. This
License explicitly affirms your unlimited permission to run the unmodified
Program. The output from running a covered work is covered by this License only
if the output, given its content, constitutes a covered work. This License
acknowledges your rights of fair use or other equivalent, as provided by
copyright law.
You may make, run and propagate covered works that you do not convey, without
conditions so long as your license otherwise remains in force. You may convey
covered works to others for the sole purpose of having them make modifications
exclusively for you, or provide you with facilities for running those works,
provided that you comply with the terms of this License in conveying all
material for which you do not control copyright. Those thus making or running
the covered works for you must do so exclusively on your behalf, under your
direction and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under the
conditions stated below. Sublicensing is not allowed; section 10 makes it
unnecessary.
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological measure
under any applicable law fulfilling obligations under article 11 of the WIPO
copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
restricting circumvention of such measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention is
effected by exercising rights under this License with respect to the covered
work, and you disclaim any intention to limit operation or modification of the
work as a means of enforcing, against the work's users, your or third parties'
legal rights to forbid circumvention of technological measures.
### 4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you receive it,
in any medium, provided that you conspicuously and appropriately publish on
each copy an appropriate copyright notice; keep intact all notices stating that
this License and any non-permissive terms added in accord with section 7 apply
to the code; keep intact all notices of the absence of any warranty; and give
all recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey, and you may
offer support or warranty protection for a fee.
### 5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to produce it
from the Program, in the form of source code under the terms of section 4,
provided that you also meet all of these conditions:
- a) The work must carry prominent notices stating that you modified it, and
giving a relevant date.
- b) The work must carry prominent notices stating that it is released under
this License and any conditions added under section 7. This requirement
modifies the requirement in section 4 to *keep intact all notices*.
- c) You must license the entire work, as a whole, under this License to
anyone who comes into possession of a copy. This License will therefore
apply, along with any applicable section 7 additional terms, to the whole
of the work, and all its parts, regardless of how they are packaged. This
License gives no permission to license the work in any other way, but it
does not invalidate such permission if you have separately received it.
- d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your work need
not make them do so.
A compilation of a covered work with other separate and independent works,
which are not by their nature extensions of the covered work, and which are not
combined with it such as to form a larger program, in or on a volume of a
storage or distribution medium, is called an *aggregate* if the compilation and
its resulting copyright are not used to limit the access or legal rights of the
compilation's users beyond what the individual works permit. Inclusion of a
covered work in an aggregate does not cause this License to apply to the other
parts of the aggregate.
### 6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms of sections 4
and 5, provided that you also convey the machine-readable Corresponding Source
under the terms of this License, in one of these ways:
- a) Convey the object code in, or embodied in, a physical product (including
a physical distribution medium), accompanied by the Corresponding Source
fixed on a durable physical medium customarily used for software
interchange.
- b) Convey the object code in, or embodied in, a physical product (including
a physical distribution medium), accompanied by a written offer, valid for
at least three years and valid for as long as you offer spare parts or
customer support for that product model, to give anyone who possesses the
object code either
1. a copy of the Corresponding Source for all the software in the product
that is covered by this License, on a durable physical medium
customarily used for software interchange, for a price no more than your
reasonable cost of physically performing this conveying of source, or
2. access to copy the Corresponding Source from a network server at no
charge.
- c) Convey individual copies of the object code with a copy of the written
offer to provide the Corresponding Source. This alternative is allowed only
occasionally and noncommercially, and only if you received the object code
with such an offer, in accord with subsection 6b.
- d) Convey the object code by offering access from a designated place
(gratis or for a charge), and offer equivalent access to the Corresponding
Source in the same way through the same place at no further charge. You
need not require recipients to copy the Corresponding Source along with the
object code. If the place to copy the object code is a network server, the
Corresponding Source may be on a different server operated by you or a
third party) that supports equivalent copying facilities, provided you
maintain clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the Corresponding
Source, you remain obligated to ensure that it is available for as long as
needed to satisfy these requirements.
- e) Convey the object code using peer-to-peer transmission, provided you
inform other peers where the object code and Corresponding Source of the
work are being offered to the general public at no charge under subsection
6d.
A separable portion of the object code, whose source code is excluded from the
Corresponding Source as a System Library, need not be included in conveying the
object code work.
A *User Product* is either
1. a *consumer product*, which means any tangible personal property which is
normally used for personal, family, or household purposes, or
2. anything designed or sold for incorporation into a dwelling.
In determining whether a product is a consumer product, doubtful cases shall be
resolved in favor of coverage. For a particular product received by a
particular user, *normally used* refers to a typical or common use of that
class of product, regardless of the status of the particular user or of the way
in which the particular user actually uses, or expects or is expected to use,
the product. A product is a consumer product regardless of whether the product
has substantial commercial, industrial or non-consumer uses, unless such uses
represent the only significant mode of use of the product.
*Installation Information* for a User Product means any methods, procedures,
authorization keys, or other information required to install and execute
modified versions of a covered work in that User Product from a modified
version of its Corresponding Source. The information must suffice to ensure
that the continued functioning of the modified object code is in no case
prevented or interfered with solely because modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as part of a
transaction in which the right of possession and use of the User Product is
transferred to the recipient in perpetuity or for a fixed term (regardless of
how the transaction is characterized), the Corresponding Source conveyed under
this section must be accompanied by the Installation Information. But this
requirement does not apply if neither you nor any third party retains the
ability to install modified object code on the User Product (for example, the
work has been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates for a
work that has been modified or installed by the recipient, or for the User
Product in which it has been modified or installed. Access to a network may be
denied when the modification itself materially and adversely affects the
operation of the network or violates the rules and protocols for communication
across the network.
Corresponding Source conveyed, and Installation Information provided, in accord
with this section must be in a format that is publicly documented (and with an
implementation available to the public in source code form), and must require
no special password or key for unpacking, reading or copying.
### 7. Additional Terms.
*Additional permissions* are terms that supplement the terms of this License by
making exceptions from one or more of its conditions. Additional permissions
that are applicable to the entire Program shall be treated as though they were
included in this License, to the extent that they are valid under applicable
law. If additional permissions apply only to part of the Program, that part may
be used separately under those permissions, but the entire Program remains
governed by this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option remove any
additional permissions from that copy, or from any part of it. (Additional
permissions may be written to require their own removal in certain cases when
you modify the work.) You may place additional permissions on material, added
by you to a covered work, for which you have or can give appropriate copyright
permission.
Notwithstanding any other provision of this License, for material you add to a
covered work, you may (if authorized by the copyright holders of that material)
supplement the terms of this License with terms:
- a) Disclaiming warranty or limiting liability differently from the terms of
sections 15 and 16 of this License; or
- b) Requiring preservation of specified reasonable legal notices or author
attributions in that material or in the Appropriate Legal Notices displayed
by works containing it; or
- c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in reasonable
ways as different from the original version; or
- d) Limiting the use for publicity purposes of names of licensors or authors
of the material; or
- e) Declining to grant rights under trademark law for use of some trade
names, trademarks, or service marks; or
- f) Requiring indemnification of licensors and authors of that material by
anyone who conveys the material (or modified versions of it) with
contractual assumptions of liability to the recipient, for any liability
that these contractual assumptions directly impose on those licensors and
authors.
All other non-permissive additional terms are considered *further restrictions*
within the meaning of section 10. If the Program as you received it, or any
part of it, contains a notice stating that it is governed by this License along
with a term that is a further restriction, you may remove that term. If a
license document contains a further restriction but permits relicensing or
conveying under this License, you may add to a covered work material governed
by the terms of that license document, provided that the further restriction
does not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you must place,
in the relevant source files, a statement of the additional terms that apply to
those files, or a notice indicating where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the form of a
separately written license, or stated as exceptions; the above requirements
apply either way.
### 8. Termination.
You may not propagate or modify a covered work except as expressly provided
under this License. Any attempt otherwise to propagate or modify it is void,
and will automatically terminate your rights under this License (including any
patent licenses granted under the third paragraph of section 11).
However, if you cease all violation of this License, then your license from a
particular copyright holder is reinstated
- a) provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and
- b) permanently, if the copyright holder fails to notify you of the
violation by some reasonable means prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is reinstated
permanently if the copyright holder notifies you of the violation by some
reasonable means, this is the first time you have received notice of violation
of this License (for any work) from that copyright holder, and you cure the
violation prior to 30 days after your receipt of the notice.
Termination of your rights under this section does not terminate the licenses
of parties who have received copies or rights from you under this License. If
your rights have been terminated and not permanently reinstated, you do not
qualify to receive new licenses for the same material under section 10.
### 9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or run a copy
of the Program. Ancillary propagation of a covered work occurring solely as a
consequence of using peer-to-peer transmission to receive a copy likewise does
not require acceptance. However, nothing other than this License grants you
permission to propagate or modify any covered work. These actions infringe
copyright if you do not accept this License. Therefore, by modifying or
propagating a covered work, you indicate your acceptance of this License to do
so.
### 10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically receives a
license from the original licensors, to run, modify and propagate that work,
subject to this License. You are not responsible for enforcing compliance by
third parties with this License.
An *entity transaction* is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered work
results from an entity transaction, each party to that transaction who receives
a copy of the work also receives whatever licenses to the work the party's
predecessor in interest had or could give under the previous paragraph, plus a
right to possession of the Corresponding Source of the work from the
predecessor in interest, if the predecessor has it or can get it with
reasonable efforts.
You may not impose any further restrictions on the exercise of the rights
granted or affirmed under this License. For example, you may not impose a
license fee, royalty, or other charge for exercise of rights granted under this
License, and you may not initiate litigation (including a cross-claim or
counterclaim in a lawsuit) alleging that any patent claim is infringed by
making, using, selling, offering for sale, or importing the Program or any
portion of it.
### 11. Patents.
A *contributor* is a copyright holder who authorizes use under this License of
the Program or a work on which the Program is based. The work thus licensed is
called the contributor's *contributor version*.
A contributor's *essential patent claims* are all patent claims owned or
controlled by the contributor, whether already acquired or hereafter acquired,
that would be infringed by some manner, permitted by this License, of making,
using, or selling its contributor version, but do not include claims that would
be infringed only as a consequence of further modification of the contributor
version. For purposes of this definition, *control* includes the right to grant
patent sublicenses in a manner consistent with the requirements of this
License.
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
license under the contributor's essential patent claims, to make, use, sell,
offer for sale, import and otherwise run, modify and propagate the contents of
its contributor version.
In the following three paragraphs, a *patent license* is any express agreement
or commitment, however denominated, not to enforce a patent (such as an express
permission to practice a patent or covenant not to sue for patent
infringement). To *grant* such a patent license to a party means to make such
an agreement or commitment not to enforce a patent against the party.
If you convey a covered work, knowingly relying on a patent license, and the
Corresponding Source of the work is not available for anyone to copy, free of
charge and under the terms of this License, through a publicly available
network server or other readily accessible means, then you must either
1. cause the Corresponding Source to be so available, or
2. arrange to deprive yourself of the benefit of the patent license for this
particular work, or
3. arrange, in a manner consistent with the requirements of this License, to
extend the patent license to downstream recipients.
*Knowingly relying* means you have actual knowledge that, but for the patent
license, your conveying the covered work in a country, or your recipient's use
of the covered work in a country, would infringe one or more identifiable
patents in that country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or arrangement, you
convey, or propagate by procuring conveyance of, a covered work, and grant a
patent license to some of the parties receiving the covered work authorizing
them to use, propagate, modify or convey a specific copy of the covered work,
then the patent license you grant is automatically extended to all recipients
of the covered work and works based on it.
A patent license is *discriminatory* if it does not include within the scope of
its coverage, prohibits the exercise of, or is conditioned on the non-exercise
of one or more of the rights that are specifically granted under this License.
You may not convey a covered work if you are a party to an arrangement with a
third party that is in the business of distributing software, under which you
make payment to the third party based on the extent of your activity of
conveying the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory patent
license
- a) in connection with copies of the covered work conveyed by you (or copies
made from those copies), or
- b) primarily for and in connection with specific products or compilations
that contain the covered work, unless you entered into that arrangement, or
that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting any implied
license or other defenses to infringement that may otherwise be available to
you under applicable patent law.
### 12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not excuse
you from the conditions of this License. If you cannot convey a covered work so
as to satisfy simultaneously your obligations under this License and any other
pertinent obligations, then as a consequence you may not convey it at all. For
example, if you agree to terms that obligate you to collect a royalty for
further conveying from those to whom you convey the Program, the only way you
could satisfy both those terms and this License would be to refrain entirely
from conveying the Program.
### 13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have permission to
link or combine any covered work with a work licensed under version 3 of the
GNU Affero General Public License into a single combined work, and to convey
the resulting work. The terms of this License will continue to apply to the
part which is the covered work, but the special requirements of the GNU Affero
General Public License, section 13, concerning interaction through a network
will apply to the combination as such.
### 14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of the GNU
General Public License from time to time. Such new versions will be similar in
spirit to the present version, but may differ in detail to address new problems
or concerns.
Each version is given a distinguishing version number. If the Program specifies
that a certain numbered version of the GNU General Public License *or any later
version* applies to it, you have the option of following the terms and
conditions either of that numbered version or of any later version published by
the Free Software Foundation. If the Program does not specify a version number
of the GNU General Public License, you may choose any version ever published by
the Free Software Foundation.
If the Program specifies that a proxy can decide which future versions of the
GNU General Public License can be used, that proxy's public statement of
acceptance of a version permanently authorizes you to choose that version for
the Program.
Later license versions may give you additional or different permissions.
However, no additional obligations are imposed on any author or copyright
holder as a result of your choosing to follow a later version.
### 15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
CORRECTION.
### 16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
### 17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided above cannot
be given local legal effect according to their terms, reviewing courts shall
apply local law that most closely approximates an absolute waiver of all civil
liability in connection with the Program, unless a warranty or assumption of
liability accompanies a copy of the Program in return for a fee.
## END OF TERMS AND CONDITIONS ###
### How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible
use to the public, the best way to achieve this is to make it free software
which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach
them to the start of each source file to most effectively state the exclusion
of warranty; and each file should have at least the *copyright* line and a
pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short notice like
this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w` and `show c` should show the appropriate
parts of the General Public License. Of course, your program's commands might
be different; for a GUI interface, you would use an *about box*.
You should also get your employer (if you work as a programmer) or school, if
any, to sign a *copyright disclaimer* for the program, if necessary. For more
information on this, and how to apply and follow the GNU GPL, see
[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
The GNU General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may consider
it more useful to permit linking proprietary applications with the library. If
this is what you want to do, use the GNU Lesser General Public License instead
of this License. But first, please read
[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html).

443
lib/playfair/hand_garble.c Executable file
View File

@@ -0,0 +1,443 @@
#include <stdint.h>
#include <stdio.h>
#define printf(...) (void)0;
uint8_t rol8(uint8_t x, int y);
uint32_t rol8x(uint8_t x, int y);
uint32_t weird_ror8(uint8_t input, int count)
{
if (count == 0)
return 0;
return ((input >> count) & 0xff) | (input & 0xff) << (8-count);
}
uint32_t weird_rol8(uint8_t input, int count)
{
if (count == 0)
return 0;
return ((input << count) & 0xff) | (input & 0xff) >> (8-count);
}
uint32_t weird_rol32(uint8_t input, int count)
{
if (count == 0)
return 0;
return (input << count) ^ (input >> (8 - count));
}
// I do not know why it is doing all of this, and there is still a possibility for a gremlin or two to be lurking in the background
// I DO know it is not trivial. It could be purely random garbage, of course.
void garble(unsigned char* buffer0, unsigned char* buffer1, unsigned char* buffer2, unsigned char* buffer3, unsigned char* buffer4)
{
unsigned int tmp, tmp2, tmp3;
unsigned int A, B, C, D, E, M, J, G, F, H, K, R, S, T, U, V, W, X, Y, Z;
// buffer1[64] = A
// (buffer1[99] / 3) = B
// 0ABAAABB
// Then we AND with a complex expression, and add 20 just for good measure
buffer2[12] = 0x14 + (((buffer1[64] & 92) | ((buffer1[99] / 3) & 35)) & buffer4[rol8x(buffer4[(buffer1[206] % 21)],4) % 21]);
printf("buffer2[12] = %02x\n", buffer2[12]);
// This is a bit simpler: 2*B*B/25
buffer1[4] = (buffer1[99] / 5) * (buffer1[99] / 5) * 2;
printf("buffer1[4] = %02x\n", buffer1[4]);
// Simpler still!
buffer2[34] = 0xb8;
printf("buffer2[34] = %02x\n", buffer2[34]);
// ...
buffer1[153] ^= (buffer2[buffer1[203] % 35] * buffer2[buffer1[203] % 35] * buffer1[190]);
printf("buffer1[153] = %02x\n", buffer1[153]);
// This one looks simple, but wow was it not :(
buffer0[3] -= (((buffer4[buffer1[205] % 21]>>1) & 80) | 0xe6440);
printf("buffer0[3] = %02x\n", buffer0[3]);
// This is always 0x93
buffer0[16] = 0x93;
printf("buffer0[16] = %02x\n", buffer0[16]);
// This is always 0x62
buffer0[13] = 0x62;
printf("buffer0[13] = %02x\n", buffer0[13]);
buffer1[33] -= (buffer4[buffer1[36] % 21] & 0xf6);
printf("buffer1[33] = %02x\n", buffer1[33]);
// This is always 7
tmp2 = buffer2[buffer1[67] % 35];
buffer2[12] = 0x07;
printf("buffer2[12] = %02x\n", buffer2[12]);
// This is pretty easy!
tmp = buffer0[buffer1[181] % 20];
buffer1[2] -= 3136;
printf("buffer1[2] = %02x\n", buffer1[2]);
buffer0[19] = buffer4[buffer1[58] % 21];
printf("buffer0[19] = %02x\n", buffer0[19]);
buffer3[0] = 92 - buffer2[buffer1[32] % 35];
printf("buffer3[0] = %02x\n", buffer3[0]);
buffer3[4] = buffer2[buffer1[15] % 35] + 0x9e;
printf("buffer3[4] = %02x\n", buffer3[4]);
buffer1[34] += (buffer4[((buffer2[buffer1[15] % 35] + 0x9e) & 0xff) % 21] / 5);
printf("buffer1[34] = %02x\n", buffer1[34]);
buffer0[19] += 0xfffffee6 - ((buffer0[buffer3[4] % 20]>>1) & 102);
printf("buffer0[19] = %02x\n", buffer0[19]);
// This LOOKS like it should be a rol8x, but it just doesnt work out because if the shift amount is 0, then the output is 0 too :(
// FIXME: Switch to weird_ror8
buffer1[15] = (3*(((buffer1[72] >> (buffer4[buffer1[190] % 21] & 7)) ^ (buffer1[72] << ((7 - (buffer4[buffer1[190] % 21]-1)&7)))) - (3*buffer4[buffer1[126] % 21]))) ^ buffer1[15];
printf("buffer1[15] = %02x\n", buffer1[15]);
buffer0[15] ^= buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35];
printf("buffer0[15] = %02x\n", buffer0[15]);
buffer2[4] ^= buffer1[202]/3;
printf("buffer2[4] = %02x\n", buffer2[4]);
// This could probably be quite a bit simpler.
A = 92 - buffer0[buffer3[0] % 20];
E = (A & 0xc6) | (~buffer1[105] & 0xc6) | (A & (~buffer1[105]));
buffer2[1] += (E*E*E);
printf("buffer2[1] = %02x\n", buffer2[1]);
buffer0[19] ^= ((224 | (buffer4[buffer1[92] % 21] & 27)) * buffer2[buffer1[41] % 35]) / 3;
printf("buffer0[19] = %02x\n", buffer0[19]);
buffer1[140] += weird_ror8(92, buffer1[5] & 7);
printf("buffer1[140] = %02x\n", buffer1[140]);
// Is this as simple as it could be?
buffer2[12] += ((((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) | buffer1[182]) & 192) | (((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) & buffer1[182]);
printf("buffer2[12] = %02x\n", buffer2[12]);
buffer1[36] += 125;
printf("buffer1[36] = %02x\n", buffer1[36]);
buffer1[124] = rol8x((((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15])) & buffer0[buffer1[43] % 20]) | (((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15]) | buffer0[buffer1[43] % 20]) & 95), 4);
printf("buffer1[124] = %02x\n", buffer1[124]);
buffer3[8] = ((((buffer0[buffer3[4] % 20] & 95)) & ((buffer4[buffer1[68] % 21] & 46) << 1)) | 16) ^ 92;
printf("buffer3[8] = %02x\n", buffer3[8]);
A = buffer1[177] + buffer4[buffer1[79] % 21];
D = (((A >> 1) | ((3 * buffer1[148]) / 5)) & buffer2[1]) | ((A >> 1) & ((3 * buffer1[148])/5));
buffer3[12] = ((-34 - D));
printf("buffer3[12] = %02x\n", buffer3[12]);
A = 8 - ((buffer2[22] & 7)); // FIXME: buffer2[22] = 74, so A is always 6 and B^C is just ror8(buffer1[33], 6)
B = (buffer1[33] >> (A & 7));
C = buffer1[33] << (buffer2[22] & 7);
buffer2[16] += ((buffer2[buffer3[0] % 35] & 159) | buffer0[buffer3[4] % 20] | 8) - ((B^C) | 128);
printf("buffer2[16] = %02x\n", buffer2[16]);
// This one was very easy so I just skipped ahead and did it
buffer0[14] ^= buffer2[buffer3[12] % 35];
printf("buffer0[14] = %02x\n", buffer0[14]);
// Monster goes here
A = weird_rol8(buffer4[buffer0[buffer1[201] % 20] % 21], ((buffer2[buffer1[112] % 35] << 1) & 7));
D = (buffer0[buffer1[208] % 20] & 131) | (buffer0[buffer1[164] % 20] & 124);
buffer1[19] += (A & (D/5)) | ((A | (D/5)) & 37);
printf("buffer1[19] = %02x\n", buffer1[19]);
buffer2[8] = weird_ror8(140, ((buffer4[buffer1[45] % 21] + 92) * (buffer4[buffer1[45] % 21] + 92)) & 7);
printf("buffer2[8] = %02x\n", buffer2[8]);
buffer1[190] = 56;
printf("buffer1[190] = %02x\n", buffer1[190]);
buffer2[8] ^= buffer3[0];
printf("buffer2[8] = %02x\n", buffer2[8]);
buffer1[53] = ~((buffer0[buffer1[83] % 20] | 204)/5);
printf("buffer1[53] = %02x\n", buffer1[53]);
buffer0[13] += buffer0[buffer1[41] % 20];
printf("buffer0[13] = %02x\n", buffer0[13]);
buffer0[10] = ((buffer2[buffer3[0] % 35] & buffer1[2]) | ((buffer2[buffer3[0] % 35] | buffer1[2]) & buffer3[12])) / 15;
printf("buffer0[10] = %02x\n", buffer0[10]);
A = (((56 | (buffer4[buffer1[2] % 21] & 68)) | buffer2[buffer3[8] % 35]) & 42) | (((buffer4[buffer1[2] % 21] & 68) | 56) & buffer2[buffer3[8] % 35]);
buffer3[16] = (A*A) + 110;
printf("buffer3[16] = %02x\n", buffer3[16]);
buffer3[20] = 202 - buffer3[16];
printf("buffer3[20] = %02x\n", buffer3[20]);
buffer3[24] = buffer1[151];
printf("buffer3[24] = %02x\n", buffer3[24]);
buffer2[13] ^= buffer4[buffer3[0] % 21];
printf("buffer2[13] = %02x\n", buffer2[13]);
B = ((buffer2[buffer1[179] % 35] - 38) & 177) | (buffer3[12] & 177);
C = ((buffer2[buffer1[179] % 35] - 38)) & buffer3[12];
buffer3[28] = 30 + ((B | C) * (B | C));
printf("buffer3[28] = %02x\n", buffer3[28]);
buffer3[32] = buffer3[28] + 62;
printf("buffer3[32] = %02x\n", buffer3[32]);
// eek
A = ((buffer3[20] + (buffer3[0] & 74)) | ~buffer4[buffer3[0] % 21]) & 121;
B = ((buffer3[20] + (buffer3[0] & 74)) & ~buffer4[buffer3[0] % 21]);
tmp3 = (A|B);
C = ((((A|B) ^ 0xffffffa6) | buffer3[0]) & 4) | (((A|B) ^ 0xffffffa6) & buffer3[0]);
buffer1[47] = (buffer2[buffer1[89] % 35] + C) ^ buffer1[47];
printf("buffer1[47] = %02x\n", buffer1[47]);
buffer3[36] = ((rol8((tmp & 179) + 68, 2) & buffer0[3]) | (tmp2 & ~buffer0[3])) - 15;
printf("buffer3[36] = %02x\n", buffer3[36]);
buffer1[123] ^= 221;
printf("buffer1[123] = %02x\n", buffer1[123]);
A = ((buffer4[buffer3[0] % 21]) / 3) - buffer2[buffer3[4] % 35];
C = (((buffer3[0] & 163) + 92) & 246) | (buffer3[0] & 92);
E = ((C | buffer3[24]) & 54) | (C & buffer3[24]);
buffer3[40] = A - E;
printf("buffer3[40] = %02x\n", buffer3[40]);
buffer3[44] = tmp3 ^ 81 ^ (((buffer3[0] >> 1) & 101) + 26);
printf("buffer3[44] = %02x\n", buffer3[44]);
buffer3[48] = buffer2[buffer3[4] % 35] & 27;
printf("buffer3[48] = %02x\n", buffer3[48]);
buffer3[52] = 27;
printf("buffer3[52] = %02x\n", buffer3[52]);
buffer3[56] = 199;
printf("buffer3[56] = %02x\n", buffer3[56]);
// caffeine
buffer3[64] = buffer3[4] + (((((((buffer3[40] | buffer3[24]) & 177) | (buffer3[40] & buffer3[24])) & ((((buffer4[buffer3[0] % 20] & 177) | 176)) | ((buffer4[buffer3[0] % 21]) & ~3))) | ((((buffer3[40] & buffer3[24]) | ((buffer3[40] | buffer3[24]) & 177)) & 199) | ((((buffer4[buffer3[0] % 21] & 1) + 176) | (buffer4[buffer3[0] % 21] & ~3)) & buffer3[56]))) & (~buffer3[52])) | buffer3[48]);
printf("buffer3[64] = %02x (want E7)\n", buffer3[64]);
buffer2[33] ^= buffer1[26];
printf("buffer2[33] = %02x\n", buffer2[33]);
buffer1[106] ^= buffer3[20] ^ 133;
printf("buffer1[106] = %02x\n", buffer1[106]);
buffer2[30] = ((buffer3[64] / 3) - (275 | (buffer3[0] & 247))) ^ buffer0[buffer1[122] % 20];
printf("buffer2[130] = %02x\n", buffer2[30]);
buffer1[22] = (buffer2[buffer1[90] % 35] & 95) | 68;
printf("buffer1[22] = %02x\n", buffer1[22]);
A = (buffer4[buffer3[36] % 21] & 184) | (buffer2[buffer3[44] % 35] & ~184);
buffer2[18] += ((A*A*A) >> 1);
printf("buffer2[18] = %02x\n", buffer2[18]);
buffer2[5] -= buffer4[buffer1[92] % 21];
printf("buffer2[5] = %02x\n", buffer2[5]);
A = (((buffer1[41] & ~24)|(buffer2[buffer1[183] % 35] & 24)) & (buffer3[16] + 53)) | (buffer3[20] & buffer2[buffer3[20] % 35]);
B = (buffer1[17] & (~buffer3[44])) | (buffer0[buffer1[59] % 20] & buffer3[44]);
buffer2[18] ^= (A*B);
printf("buffer2[18] = %02x\n", buffer2[18]);
A = weird_ror8(buffer1[11], buffer2[buffer1[28] % 35] & 7) & 7;
B = (((buffer0[buffer1[93] % 20] & ~buffer0[14]) | (buffer0[14] & 150)) & ~28) | (buffer1[7] & 28);
buffer2[22] = (((((B | weird_rol8(buffer2[buffer3[0] % 35], A)) & buffer2[33]) | (B & weird_rol8(buffer2[buffer3[0] % 35], A))) + 74) & 0xff);
printf("buffer2[22] = %02x\n", buffer2[22]);
A = buffer4[(buffer0[buffer1[39] % 20] ^ 217) % 21]; // X5
buffer0[15] -= ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0])) & A) | ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0]) | A) & buffer3[32]);
printf("buffer0[15] = %02x\n", buffer0[15]);
// We need to save T here, and boy is it complicated to calculate!
B = (((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer0[buffer3[64] % 20] | buffer2[buffer1[57] % 35]) & 95) | (buffer3[64] & 45) | 82) & 32);
C = ((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer2[buffer1[57] % 35] | buffer0[buffer3[64] % 20]) & 95)) & ((buffer3[64] & 45) | 82);
D = ((((buffer3[0]/3) - (buffer3[64]|buffer1[22]))) ^ (buffer3[28] + 62) ^ ((B|C)));
T = buffer0[(D & 0xff) % 20];
buffer3[68] = (buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20]) | buffer2[buffer3[64] % 35];
printf("buffer3[68] = %02x\n", buffer3[68]);
U = buffer0[buffer1[50] % 20]; // this is also v100
W = buffer2[buffer1[138] % 35];
X = buffer4[buffer1[39] % 21];
Y = buffer0[buffer1[4] % 20]; // this is also v120
Z = buffer4[buffer1[202] % 21]; // also v124
V = buffer0[buffer1[151] % 20];
S = buffer2[buffer1[14] % 35];
R = buffer0[buffer1[145] % 20];
A = (buffer2[buffer3[68] % 35] & buffer0[buffer1[209] % 20]) | ((buffer2[buffer3[68] % 35] | buffer0[buffer1[209] % 20]) & 24);
B = weird_rol8(buffer4[buffer1[127] % 21], buffer2[buffer3[68] % 35] & 7);
C = (A & buffer0[10]) | (B & ~buffer0[10]);
D = 7 ^ (buffer4[buffer2[buffer3[36] % 35] % 21] << 1);
buffer3[72] = (C & 71) | (D & ~71);
printf("buffer3[72] = %02x\n", buffer3[72]);
buffer2[2] += (((buffer0[buffer3[20] % 20] << 1) & 159) | (buffer4[buffer1[190] % 21] & ~159)) & ((((buffer4[buffer3[64] % 21] & 110) | (buffer0[buffer1[25] % 20] & ~110)) & ~150) | (buffer1[25] & 150));
printf("buffer2[2] = %02x\n", buffer2[2]);
buffer2[14] -= ((buffer2[buffer3[20] % 35] & (buffer3[72] ^ buffer2[buffer1[100] % 35])) & ~34) | (buffer1[97] & 34);
printf("buffer2[14] = %02x\n", buffer2[14]);
buffer0[17] = 115;
printf("buffer0[17] = %02x\n", buffer0[17]);
buffer1[23] ^= ((((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20])) & (buffer1[50]/3)) |
((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20]) | (buffer1[50] / 3)) & 246)) << 1);
printf("buffer1[23] = %02x\n", buffer1[23]);
buffer0[13] = ((((((buffer0[buffer3[40] % 20] | buffer1[10]) & 82) | (buffer0[buffer3[40] % 20] & buffer1[10])) & 209) |
((buffer0[buffer1[39] % 20] << 1) & 46)) >> 1);
printf("buffer0[13] = %02x\n", buffer0[13]);
buffer2[33] -= buffer1[113] & 9;
printf("buffer2[33] = %02x\n", buffer2[33]);
buffer2[28] -= ((((2 | (buffer1[110] & 222)) >> 1) & ~223) | (buffer3[20] & 223));
printf("buffer2[28] = %02x\n", buffer2[28]);
J = weird_rol8((V | Z), (U & 7)); // OK
A = (buffer2[16] & T) | (W & (~buffer2[16]));
B = (buffer1[33] & 17) | (X & ~17);
E = ((Y | ((A+B) / 5)) & 147) |
(Y & ((A+B) / 5)); // OK
M = (buffer3[40] & buffer4[((buffer3[8] + J + E) & 0xff) % 21]) |
((buffer3[40] | buffer4[((buffer3[8] + J + E) & 0xff) % 21]) & buffer2[23]);
buffer0[15] = (((buffer4[buffer3[20] % 21] - 48) & (~buffer1[184])) | ((buffer4[buffer3[20] % 21] - 48) & 189) | (189 & ~buffer1[184])) & (M*M*M);
printf("buffer0[15] = %02x\n", buffer0[15]);
buffer2[22] += buffer1[183];
printf("buffer2[22] = %02x\n", buffer2[22]);
buffer3[76] = (3 * buffer4[buffer1[1] % 21]) ^ buffer3[0];
printf("buffer3[76] = %02x\n", buffer3[76]);
A = buffer2[((buffer3[8] + (J + E)) & 0xff) % 35];
F = (((buffer4[buffer1[178] % 21] & A) | ((buffer4[buffer1[178] % 21] | A) & 209)) * buffer0[buffer1[13] % 20]) * (buffer4[buffer1[26] % 21] >> 1);
G = (F + 0x733ffff9) * 198 - (((F + 0x733ffff9) * 396 + 212) & 212) + 85;
buffer3[80] = buffer3[36] + (G ^ 148) + ((G ^ 107) << 1) - 127;
printf("buffer3[80] = %02x\n", buffer3[80]);
buffer3[84] = ((buffer2[buffer3[64] % 35]) & 245) | (buffer2[buffer3[20] % 35] & 10);
printf("buffer3[84] = %02x\n", buffer3[84]);
A = buffer0[buffer3[68] % 20] | 81;
buffer2[18] -= ((A*A*A) & ~buffer0[15]) | ((buffer3[80] / 15) & buffer0[15]);
printf("buffer2[18] = %02x\n", buffer2[18]);
buffer3[88] = buffer3[8] + J + E - buffer0[buffer1[160] % 20] + (buffer4[buffer0[((buffer3[8] + J + E) & 255) % 20] % 21] / 3);
printf("buffer3[88] = %02x\n", buffer3[88]);
B = ((R ^ buffer3[72]) & ~198) | ((S * S) & 198);
F = (buffer4[buffer1[69] % 21] & buffer1[172]) | ((buffer4[buffer1[69] % 21] | buffer1[172] ) & ((buffer3[12] - B) + 77));
buffer0[16] = 147 - ((buffer3[72] & ((F & 251) | 1)) | (((F & 250) | buffer3[72]) & 198));
printf("buffer0[16] = %02x\n", buffer0[16]);
C = (buffer4[buffer1[168] % 21] & buffer0[buffer1[29] % 20] & 7) | ((buffer4[buffer1[168] % 21] | buffer0[buffer1[29] % 20]) & 6);
F = (buffer4[buffer1[155] % 21] & buffer1[105]) | ((buffer4[buffer1[155] % 21] | buffer1[105]) & 141);
buffer0[3] -= buffer4[weird_rol32(F, C) % 21];
printf("buffer0[3] = %02x\n", buffer0[3]);
buffer1[5] = weird_ror8(buffer0[12], ((buffer0[buffer1[61] % 20] / 5) & 7)) ^ (((~buffer2[buffer3[84] % 35]) & 0xffffffff) / 5);
printf("buffer1[5] = %02x\n", buffer1[5]);
buffer1[198] += buffer1[3];
printf("buffer1[198] = %02x\n", buffer1[198]);
A = (162 | buffer2[buffer3[64] % 35]);
buffer1[164] += ((A*A)/5);
printf("buffer1[164] = %02x\n", buffer1[164]);
G = weird_ror8(139, (buffer3[80] & 7));
C = ((buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21]) & 95) | (buffer0[buffer3[40] % 20] & ~95);
buffer3[92] = (G & 12) | (buffer0[buffer3[20] % 20] & 12) | (G & buffer0[buffer3[20] % 20]) | C;
printf("buffer3[92] = %02x\n", buffer3[92]);
buffer2[12] += ((buffer1[103] & 32) | (buffer3[92] & ((buffer1[103] | 60))) | 16)/3;
printf("buffer2[12] = %02x\n", buffer2[12]);
buffer3[96] = buffer1[143];
printf("buffer3[96] = %02x\n", buffer3[96]);
buffer3[100] = 27;
printf("buffer3[100] = %02x\n", buffer3[100]);
buffer3[104] = (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) ^ 119;
printf("buffer3[104] = %02x\n", buffer3[104]);
buffer3[108] = 238 & ((((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) << 1);
printf("buffer3[108] = %02x\n", buffer3[108]);
buffer3[112] = (~buffer3[64] & (buffer3[84] / 3)) ^ 49;
printf("buffer3[112] = %02x\n", buffer3[112]);
buffer3[116] = 98 & ((~buffer3[64] & (buffer3[84] / 3)) << 1);
printf("buffer3[116] = %02x\n", buffer3[116]);
// finale
A = (buffer1[35] & buffer2[8]) | (buffer3[40] & ~buffer2[8]);
B = (A & buffer3[64]) | (((buffer3[84] / 3) & ~buffer3[64]));
buffer1[143] = buffer3[96] - ((B & (86 + ((buffer1[172] & 64) >> 1))) | (((((buffer1[172] & 65) >> 1) ^ 86) | ((~buffer3[64] & (buffer3[84] / 3)) | (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]))) & buffer3[100]));
printf("buffer1[143] = %02x\n", buffer1[143]);
buffer2[29] = 162;
printf("buffer2[29] = %02x\n", buffer2[29]);
A = ((((buffer4[buffer3[88] % 21]) & 160) | (buffer0[buffer1[125] % 20] & 95)) >> 1);
B = buffer2[buffer1[149] % 35] ^ (buffer1[43] * buffer1[43]);
buffer0[15] += (B&A) | ((A|B) & 115);
printf("buffer0[15] = %02x\n", buffer0[15]);
buffer3[120] = buffer3[64] - buffer0[buffer3[40] % 20];
printf("buffer3[120] = %02x\n", buffer3[120]);
buffer1[95] = buffer4[buffer3[20] % 21];
printf("buffer1[95] = %02x\n", buffer1[95]);
A = weird_ror8(buffer2[buffer3[80] % 35], (buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35]) & 7);
buffer0[7] -= (A*A);
printf("buffer0[7] = %02x\n", buffer0[7]);
buffer2[8] = buffer2[8] - buffer1[184] + (buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21]);
printf("buffer2[8] = %02x\n", buffer2[8]);
buffer0[16] = (buffer2[buffer1[102] % 35] << 1) & 132;
printf("buffer0[16] = %02x\n", buffer0[16]);
buffer3[124] = (buffer4[buffer3[40] % 21] >> 1) ^ buffer3[68];
printf("buffer3[124] = %02x\n", buffer3[124]);
buffer0[7] -= (buffer0[buffer1[191] % 20] - (((buffer4[buffer1[80] % 21] << 1) & ~177) | (buffer4[buffer4[buffer3[88] % 21] % 21] & 177)));
printf("buffer0[7] = %02x\n", buffer0[7]);
buffer0[6] = buffer0[buffer1[119] % 20];
printf("buffer0[6] = %02x\n", buffer0[6]);
A = (buffer4[buffer1[190] % 21] & ~209) | (buffer1[118] & 209);
B = buffer0[buffer3[120] % 20] * buffer0[buffer3[120] % 20];
buffer0[12] = (buffer0[buffer3[84] % 20] ^ (buffer2[buffer1[71] % 35] + buffer2[buffer1[15] % 35])) & ((A & B) | ((A | B) & 27));
printf("buffer0[12] = %02x\n", buffer0[12]);
B = (buffer1[32] & buffer2[buffer3[88] % 35]) | ((buffer1[32] | buffer2[buffer3[88] % 35]) & 23);
D = (((buffer4[buffer1[57] % 21] * 231) & 169) | (B & 86));
F = (((buffer0[buffer1[82] % 20] & ~29) | (buffer4[buffer3[124] % 21] & 29)) & 190) | (buffer4[(D/5) % 21] & ~190);
H = buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20];
K = (H & buffer1[82]) | (H & 92) | (buffer1[82] & 92);
buffer3[128] = ((F & K) | ((F | K) & 192)) ^ (D/5);
printf("buffer3[128] = %02x\n", buffer3[128]);
buffer2[25] ^= ((buffer0[buffer3[120] % 20] << 1) * buffer1[5]) - (weird_rol8(buffer3[76], (buffer4[buffer3[124] % 21] & 7)) & (buffer3[20] + 110));
printf("buffer2[25] = %02x\n", buffer2[25]);
//exit(0);
}

119
lib/playfair/modified_md5.c Executable file
View File

@@ -0,0 +1,119 @@
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#define printf(...) (void)0;
int shift[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
uint32_t F(uint32_t B, uint32_t C, uint32_t D)
{
return (B & C) | (~B & D);
}
uint32_t G(uint32_t B, uint32_t C, uint32_t D)
{
return (B & D) | (C & ~D);
}
uint32_t H(uint32_t B, uint32_t C, uint32_t D)
{
return B ^ C ^ D;
}
uint32_t I(uint32_t B, uint32_t C, uint32_t D)
{
return C ^ (B | ~D);
}
uint32_t rol(uint32_t input, int count)
{
return ((input << count) & 0xffffffff) | (input & 0xffffffff) >> (32-count);
}
void swap(uint32_t* a, uint32_t* b)
{
printf("%08x <-> %08x\n", *a, *b);
uint32_t c = *a;
*a = *b;
*b = c;
}
void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut)
{
unsigned char blockIn[64];
uint32_t* block_words = (uint32_t*)blockIn;
uint32_t* key_words = (uint32_t*)keyIn;
uint32_t* out_words = (uint32_t*)keyOut;
uint32_t A, B, C, D, Z, tmp;
int i;
memcpy(blockIn, originalblockIn, 64);
// Each cycle does something like this:
A = key_words[0];
B = key_words[1];
C = key_words[2];
D = key_words[3];
for (i = 0; i < 64; i++)
{
uint32_t input;
int j;
if (i < 16)
j = i;
else if (i < 32)
j = (5*i + 1) % 16;
else if (i < 48)
j = (3*i + 5) % 16;
else if (i < 64)
j = 7*i % 16;
input = blockIn[4*j] << 24 | blockIn[4*j+1] << 16 | blockIn[4*j+2] << 8 | blockIn[4*j+3];
printf("Key = %08x\n", A);
Z = A + input + (int)(long long)((1LL << 32) * fabs(sin(i + 1)));
if (i < 16)
Z = rol(Z + F(B,C,D), shift[i]);
else if (i < 32)
Z = rol(Z + G(B,C,D), shift[i]);
else if (i < 48)
Z = rol(Z + H(B,C,D), shift[i]);
else if (i < 64)
Z = rol(Z + I(B,C,D), shift[i]);
if (i == 63)
printf("Ror is %08x\n", Z);
printf("Output of round %d: %08X + %08X = %08X (shift %d, constant %08X)\n", i, Z, B, Z+B, shift[i], (int)(long long)((1LL << 32) * fabs(sin(i + 1))));
Z = Z + B;
tmp = D;
D = C;
C = B;
B = Z;
A = tmp;
if (i == 31)
{
// swapsies
swap(&block_words[A & 15], &block_words[B & 15]);
swap(&block_words[C & 15], &block_words[D & 15]);
swap(&block_words[(A & (15<<4))>>4], &block_words[(B & (15<<4))>>4]);
swap(&block_words[(A & (15<<8))>>8], &block_words[(B & (15<<8))>>8]);
swap(&block_words[(A & (15<<12))>>12], &block_words[(B & (15<<12))>>12]);
}
}
printf("%08X %08X %08X %08X\n", A, B, C, D);
// Now we can actually compute the output
printf("Out:\n");
printf("%08x + %08x = %08x\n", key_words[0], A, key_words[0] + A);
printf("%08x + %08x = %08x\n", key_words[1], B, key_words[1] + B);
printf("%08x + %08x = %08x\n", key_words[2], C, key_words[2] + C);
printf("%08x + %08x = %08x\n", key_words[3], D, key_words[3] + D);
out_words[0] = key_words[0] + A;
out_words[1] = key_words[1] + B;
out_words[2] = key_words[2] + C;
out_words[3] = key_words[3] + D;
}

540
lib/playfair/omg_hax.c Executable file
View File

@@ -0,0 +1,540 @@
void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut);
void sap_hash(unsigned char* blockIn, unsigned char* keyOut);
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "omg_hax.h"
#define printf(...) (void)0;
void xor_blocks(unsigned char* a, unsigned char* b, unsigned char* out)
{
for (int i = 0; i < 16; i++)
out[i] = a[i] ^ b[i];
}
void z_xor(unsigned char* in, unsigned char* out, int blocks)
{
for (int j = 0; j < blocks; j++)
for (int i = 0; i < 16; i++)
out[j*16+i] = in[j*16+i] ^ z_key[i];
}
void x_xor(unsigned char* in, unsigned char* out, int blocks)
{
for (int j = 0; j < blocks; j++)
for (int i = 0; i < 16; i++)
out[j*16+i] = in[j*16+i] ^ x_key[i];
}
void t_xor(unsigned char* in, unsigned char* out)
{
for (int i = 0; i < 16; i++)
out[i] = in[i] ^ t_key[i];
}
unsigned char sap_iv[] = {0x2B,0x84,0xFB,0x79,0xDA,0x75,0xB9,0x04,0x6C,0x24,0x73,0xF7,0xD1,0xC4,0xAB,0x0E,0x2B,0x84,0xFB,0x79,0x75,0xB9,0x04,0x6C,0x24,0x73};
unsigned char sap_key_material[] = {0xA1, 0x1A, 0x4A, 0x83,
0xF2, 0x7A, 0x75, 0xEE,
0xA2, 0x1A, 0x7D, 0xB8,
0x8D, 0x77, 0x92, 0xAB};
unsigned char index_mangle[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C};
unsigned char* table_index(int i)
{
return &table_s1[((31*i) % 0x28) << 8];
}
unsigned char* message_table_index(int i)
{
return &table_s2[(97*i % 144) << 8];
}
void print_block(char* msg, unsigned char* dword)
{
printf("%s", msg);
for (int i = 0; i < 16; i++)
printf("%02X ", dword[i]);
printf("\n");
}
void permute_block_1(unsigned char* block)
{
block[0] = table_s3[block[0]];
block[4] = table_s3[0x400+block[4]];
block[8] = table_s3[0x800+block[8]];
block[12] = table_s3[0xc00+block[12]];
unsigned char tmp = block[13];
block[13] = table_s3[0x100+block[9]];
block[9] = table_s3[0xd00+block[5]];
block[5] = table_s3[0x900+block[1]];
block[1] = table_s3[0x500+tmp];
tmp = block[2];
block[2] = table_s3[0xa00+block[10]];
block[10] = table_s3[0x200+tmp];
tmp = block[6];
block[6] = table_s3[0xe00+block[14]];
block[14] = table_s3[0x600+tmp];
tmp = block[3];
block[3] = table_s3[0xf00+block[7]];
block[7] = table_s3[0x300+block[11]];
block[11] = table_s3[0x700+block[15]];
block[15] = table_s3[0xb00+tmp];
print_block("Permutation complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel
}
unsigned char* permute_table_2(unsigned int i)
{
return &table_s4[((71 * i) % 144) << 8];
}
void permute_block_2(unsigned char* block, int round)
{
// round is 0..8?
printf("Permuting via table2, round %d... (block[0] = %02X)\n", round, block[0]);
block[0] = permute_table_2(round*16+0)[block[0]];
block[4] = permute_table_2(round*16+4)[block[4]];
block[8] = permute_table_2(round*16+8)[block[8]];
block[12] = permute_table_2(round*16+12)[block[12]];
unsigned char tmp = block[13];
block[13] = permute_table_2(round*16+13)[block[9]];
block[9] = permute_table_2(round*16+9)[block[5]];
block[5] = permute_table_2(round*16+5)[block[1]];
block[1] = permute_table_2(round*16+1)[tmp];
tmp = block[2];
block[2] = permute_table_2(round*16+2)[block[10]];
block[10] = permute_table_2(round*16+10)[tmp];
tmp = block[6];
block[6] = permute_table_2(round*16+6)[block[14]];
block[14] = permute_table_2(round*16+14)[tmp];
tmp = block[3];
block[3] = permute_table_2(round*16+3)[block[7]];
block[7] = permute_table_2(round*16+7)[block[11]];
block[11] = permute_table_2(round*16+11)[block[15]];
block[15] = permute_table_2(round*16+15)[tmp];
print_block("Permutation (2) complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel
}
// This COULD just be Rijndael key expansion, but with a different set of S-boxes
void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4])
{
uint32_t key_data[4];
int i;
for (i = 0; i < 11; i++)
{
key_schedule[i][0] = 0xdeadbeef;
key_schedule[i][1] = 0xdeadbeef;
key_schedule[i][2] = 0xdeadbeef;
key_schedule[i][3] = 0xdeadbeef;
}
unsigned char* buffer = (unsigned char*)key_data;
int ti = 0;
printf("Generating key schedule\n");
// G
print_block("Raw key material: ", key_material);
t_xor(key_material, buffer);
print_block("G has produced: ", buffer);
for (int round = 0; round < 11; round++)
{
printf("Starting round %d\n", round);
// H
key_schedule[round][0] = key_data[0];
printf("H has set chunk 1 of round %d %08X\n", round, key_schedule[round][0]);
printf("H complete\n");
// I
unsigned char* table1 = table_index(ti);
unsigned char* table2 = table_index(ti+1);
unsigned char* table3 = table_index(ti+2);
unsigned char* table4 = table_index(ti+3);
ti += 4;
//buffer[0] = (buffer[0] - (4 & (buffer[0] << 1)) + 2) ^ 2 ^ index_mangle[round] ^ table1[buffer[0x0d]];
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0d], table1[buffer[0x0d]]);
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0e], table2[buffer[0x0e]]);
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0f], table3[buffer[0x0f]]);
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0c], table4[buffer[0x0c]]);
buffer[0] ^= table1[buffer[0x0d]] ^ index_mangle[round];
buffer[1] ^= table2[buffer[0x0e]];
buffer[2] ^= table3[buffer[0x0f]];
buffer[3] ^= table4[buffer[0x0c]];
print_block("After I, buffer is now: ", buffer);
printf("I complete\n");
// H
key_schedule[round][1] = key_data[1];
printf("H has set chunk 2 to %08X\n", key_schedule[round][1]);
printf("H complete\n");
// J
key_data[1] ^= key_data[0];
printf("J complete\n");
print_block("Buffer is now ", buffer);
// H
key_schedule[round][2] = key_data[2];
printf("H has set chunk3 to %08X\n", key_schedule[round][2]);
printf("H complete\n");
// J
key_data[2] ^= key_data[1];
printf("J complete\n");
// K and L
// Implement K and L to fill in other bits of the key schedule
key_schedule[round][3] = key_data[3];
// J again
key_data[3] ^= key_data[2];
printf("J complete\n");
}
for (i = 0; i < 11; i++)
print_block("Schedule: ", (unsigned char*)key_schedule[i]);
}
// This MIGHT just be AES, or some variant thereof.
void cycle(unsigned char* block, uint32_t key_schedule[11][4])
{
uint32_t ptr1 = 0;
uint32_t ptr2 = 0;
uint32_t ptr3 = 0;
uint32_t ptr4 = 0;
uint32_t ab;
unsigned char* buffer = (unsigned char*)&ab;
uint32_t* bWords = (uint32_t*)block;
bWords[0] ^= key_schedule[10][0];
bWords[1] ^= key_schedule[10][1];
bWords[2] ^= key_schedule[10][2];
bWords[3] ^= key_schedule[10][3];
// First, these are permuted
permute_block_1(block);
for (int round = 0; round < 9; round++)
{
// E
// Note that table_s5 is a table of 4-byte words. Therefore we do not need to <<2 these indices
// TODO: Are these just T-tables?
unsigned char* key0 = (unsigned char*)&key_schedule[9-round][0];
ptr1 = table_s5[block[3] ^ key0[3]];
ptr2 = table_s6[block[2] ^ key0[2]];
ptr3 = table_s8[block[0] ^ key0[0]];
ptr4 = table_s7[block[1] ^ key0[1]];
// A B
ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4;
printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab);
// C
((uint32_t*)block)[0] = ab;
printf("f7 = %02X\n", block[7]);
unsigned char* key1 = (unsigned char*)&key_schedule[9-round][1];
ptr2 = table_s5[block[7] ^ key1[3]];
ptr1 = table_s6[block[6] ^ key1[2]];
ptr4 = table_s7[block[5] ^ key1[1]];
ptr3 = table_s8[block[4] ^ key1[0]];
// A B again
ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4;
printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab);
// D is a bit of a nightmare, but it is really not as complicated as you might think
unsigned char* key2 = (unsigned char*)&key_schedule[9-round][2];
unsigned char* key3 = (unsigned char*)&key_schedule[9-round][3];
((uint32_t*)block)[1] = ab;
((uint32_t*)block)[2] = table_s5[block[11] ^ key2[3]] ^
table_s6[block[10] ^ key2[2]] ^
table_s7[block[9] ^ key2[1]] ^
table_s8[block[8] ^ key2[0]];
((uint32_t*)block)[3] = table_s5[block[15] ^ key3[3]] ^
table_s6[block[14] ^ key3[2]] ^
table_s7[block[13] ^ key3[1]] ^
table_s8[block[12] ^ key3[0]];
printf("Set block2 = %08X, block3 = %08X\n", ((uint32_t*)block)[2], ((uint32_t*)block)[3]);
// In the last round, instead of the permute, we do F
permute_block_2(block, 8-round);
}
printf("Using last bit of key up: %08X xor %08X -> %08X\n", ((uint32_t*)block)[0], key_schedule[0][0], ((uint32_t*)block)[0] ^ key_schedule[0][0]);
((uint32_t*)block)[0] ^= key_schedule[0][0];
((uint32_t*)block)[1] ^= key_schedule[0][1];
((uint32_t*)block)[2] ^= key_schedule[0][2];
((uint32_t*)block)[3] ^= key_schedule[0][3];
}
void decrypt_sap(unsigned char* sapIn, unsigned char* sapOut)
{
uint32_t key_schedule[11][4];
unsigned char* iv;
print_block("Base sap: ", &sapIn[0xf0]);
z_xor(sapIn, sapOut, 16);
generate_key_schedule(sap_key_material, key_schedule);
print_block("lastSap before cycle: ", &sapOut[0xf0]);
for (int i = 0xf0; i >= 0x00; i-=0x10)
{
printf("Ready to cycle %02X\n", i);
cycle(&sapOut[i], key_schedule);
print_block("After cycling, block is: ", &sapOut[i]);
if (i > 0)
{ // xor with previous block
iv = &sapOut[i-0x10];
}
else
{ // xor with sap IV
iv = sap_iv;
}
for (int j = 0; j < 16; j++)
{
printf("%02X ^ %02X -> %02X\n", sapOut[i+j], iv[j], sapOut[i+j] ^ iv[j]);
sapOut[i+j] = sapOut[i+j] ^ iv[j];
}
printf("Decrypted SAP %02X-%02X:\n", i, i+0xf);
print_block("", &sapOut[i]);
}
// Lastly grind the whole thing through x_key. This is the last time we modify sap
x_xor(sapOut, sapOut, 16);
printf("Sap is decrypted to\n");
for (int i = 0xf0; i >= 0x00; i-=0x10)
{
printf("Final SAP %02X-%02X: ", i, i+0xf);
print_block("", &sapOut[i]);
}
}
unsigned char initial_session_key[] = {0xDC, 0xDC, 0xF3, 0xB9, 0x0B, 0x74, 0xDC, 0xFB, 0x86, 0x7F, 0xF7, 0x60, 0x16, 0x72, 0x90, 0x51};
void decrypt_key(unsigned char* decryptedSap, unsigned char* keyIn, unsigned char* iv, unsigned char* keyOut)
{
unsigned char blockIn[16];
uint32_t key_schedule[11][4];
uint32_t mode_key_schedule[11][4];
generate_key_schedule(&decryptedSap[8], key_schedule);
printf("Generating mode key:\n");
generate_key_schedule(initial_session_key, mode_key_schedule);
z_xor(keyIn, blockIn, 1);
print_block("Input to cycle is: ", blockIn);
cycle(blockIn, key_schedule);
for (int j = 0; j < 16; j++)
keyOut[j] = blockIn[j] ^ iv[j];
print_block("Output from cycle is: ", keyOut);
x_xor(keyOut, keyOut, 1);
}
void decryptMessage(unsigned char* messageIn, unsigned char* decryptedMessage)
{
unsigned char buffer[16];
int i, j;
unsigned char tmp;
uint32_t key_schedule[11][4];
int mode = messageIn[12]; // 0,1,2,3
printf("mode = %02x\n", mode);
generate_key_schedule(initial_session_key, key_schedule);
// For M0-M6 we follow the same pattern
for (i = 0; i < 8; i++)
{
// First, copy in the nth block (we must start with the last one)
for (j = 0; j < 16; j++)
{
if (mode == 3)
buffer[j] = messageIn[(0x80-0x10*i)+j];
else if (mode == 2 || mode == 1 || mode == 0)
buffer[j] = messageIn[(0x10*(i+1))+j];
}
// do this permutation and update 9 times. Could this be cycle(), or the reverse of cycle()?
for (j = 0; j < 9; j++)
{
int base = 0x80 - 0x10*j;
//print_block("About to cycle. Buffer is currently: ", buffer);
buffer[0x0] = message_table_index(base+0x0)[buffer[0x0]] ^ message_key[mode][base+0x0];
buffer[0x4] = message_table_index(base+0x4)[buffer[0x4]] ^ message_key[mode][base+0x4];
buffer[0x8] = message_table_index(base+0x8)[buffer[0x8]] ^ message_key[mode][base+0x8];
buffer[0xc] = message_table_index(base+0xc)[buffer[0xc]] ^ message_key[mode][base+0xc];
tmp = buffer[0x0d];
buffer[0xd] = message_table_index(base+0xd)[buffer[0x9]] ^ message_key[mode][base+0xd];
buffer[0x9] = message_table_index(base+0x9)[buffer[0x5]] ^ message_key[mode][base+0x9];
buffer[0x5] = message_table_index(base+0x5)[buffer[0x1]] ^ message_key[mode][base+0x5];
buffer[0x1] = message_table_index(base+0x1)[tmp] ^ message_key[mode][base+0x1];
tmp = buffer[0x02];
buffer[0x2] = message_table_index(base+0x2)[buffer[0xa]] ^ message_key[mode][base+0x2];
buffer[0xa] = message_table_index(base+0xa)[tmp] ^ message_key[mode][base+0xa];
tmp = buffer[0x06];
buffer[0x6] = message_table_index(base+0x6)[buffer[0xe]] ^ message_key[mode][base+0x6];
buffer[0xe] = message_table_index(base+0xe)[tmp] ^ message_key[mode][base+0xe];
tmp = buffer[0x3];
buffer[0x3] = message_table_index(base+0x3)[buffer[0x7]] ^ message_key[mode][base+0x3];
buffer[0x7] = message_table_index(base+0x7)[buffer[0xb]] ^ message_key[mode][base+0x7];
buffer[0xb] = message_table_index(base+0xb)[buffer[0xf]] ^ message_key[mode][base+0xb];
buffer[0xf] = message_table_index(base+0xf)[tmp] ^ message_key[mode][base+0xf];
// Now we must replace the entire buffer with 4 words that we read and xor together
uint32_t word;
uint32_t* block = (uint32_t*)buffer;
block[0] = table_s9[0x000 + buffer[0x0]] ^
table_s9[0x100 + buffer[0x1]] ^
table_s9[0x200 + buffer[0x2]] ^
table_s9[0x300 + buffer[0x3]];
block[1] = table_s9[0x000 + buffer[0x4]] ^
table_s9[0x100 + buffer[0x5]] ^
table_s9[0x200 + buffer[0x6]] ^
table_s9[0x300 + buffer[0x7]];
block[2] = table_s9[0x000 + buffer[0x8]] ^
table_s9[0x100 + buffer[0x9]] ^
table_s9[0x200 + buffer[0xa]] ^
table_s9[0x300 + buffer[0xb]];
block[3] = table_s9[0x000 + buffer[0xc]] ^
table_s9[0x100 + buffer[0xd]] ^
table_s9[0x200 + buffer[0xe]] ^
table_s9[0x300 + buffer[0xf]];
}
// Next, another permute with a different table
buffer[0x0] = table_s10[(0x0 << 8) + buffer[0x0]];
buffer[0x4] = table_s10[(0x4 << 8) + buffer[0x4]];
buffer[0x8] = table_s10[(0x8 << 8) + buffer[0x8]];
buffer[0xc] = table_s10[(0xc << 8) + buffer[0xc]];
tmp = buffer[0x0d];
buffer[0xd] = table_s10[(0xd << 8) + buffer[0x9]];
buffer[0x9] = table_s10[(0x9 << 8) + buffer[0x5]];
buffer[0x5] = table_s10[(0x5 << 8) + buffer[0x1]];
buffer[0x1] = table_s10[(0x1 << 8) + tmp];
tmp = buffer[0x02];
buffer[0x2] = table_s10[(0x2 << 8) + buffer[0xa]];
buffer[0xa] = table_s10[(0xa << 8) + tmp];
tmp = buffer[0x06];
buffer[0x6] = table_s10[(0x6 << 8) + buffer[0xe]];
buffer[0xe] = table_s10[(0xe << 8) + tmp];
tmp = buffer[0x3];
buffer[0x3] = table_s10[(0x3 << 8) + buffer[0x7]];
buffer[0x7] = table_s10[(0x7 << 8) + buffer[0xb]];
buffer[0xb] = table_s10[(0xb << 8) + buffer[0xf]];
buffer[0xf] = table_s10[(0xf << 8) + tmp];
// And finally xor with the previous block of the message, except in mode-2 where we do this in reverse
if (mode == 2 || mode == 1 || mode == 0)
{
if (i > 0)
{
xor_blocks(buffer, &messageIn[0x10*i], &decryptedMessage[0x10*i]); // remember that the first 0x10 bytes are the header
}
else
xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x10*i]);
print_block(" ", &decryptedMessage[0x10*i]);
}
else
{
if (i < 7)
xor_blocks(buffer, &messageIn[0x70 - 0x10*i], &decryptedMessage[0x70 - 0x10*i]);
else
xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x70 - 0x10*i]);
printf("Decrypted message block %02X-%02X:", 0x70 - 0x10*i, 0x70 - 0x10*i+0xf);
print_block(" ", &decryptedMessage[0x70 - 0x10*i]);
}
}
}
unsigned char static_source_1[] = {0xFA, 0x9C, 0xAD, 0x4D, 0x4B, 0x68, 0x26, 0x8C, 0x7F, 0xF3, 0x88, 0x99, 0xDE, 0x92, 0x2E, 0x95,
0x1E};
unsigned char static_source_2[] = {0xEC, 0x4E, 0x27, 0x5E, 0xFD, 0xF2, 0xE8, 0x30, 0x97, 0xAE, 0x70, 0xFB, 0xE0, 0x00, 0x3F, 0x1C,
0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x09, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00};
void swap_bytes(unsigned char* a, unsigned char *b)
{
unsigned char c = *a;
*a = *b;
*b = c;
}
void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey)
{
unsigned char decryptedMessage[128];
unsigned char newSap[320];
unsigned char Q[210];
int i;
int round;
unsigned char md5[16];
unsigned char otherHash[16];
decryptMessage(messageIn, decryptedMessage);
// Now that we have the decrypted message, we can combine it with our initial sap to form the 5 blocks needed to generate the 5 words which, when added together, give
// the session key.
memcpy(&newSap[0x000], static_source_1, 0x11);
memcpy(&newSap[0x011], decryptedMessage, 0x80);
memcpy(&newSap[0x091], &oldSap[0x80], 0x80);
memcpy(&newSap[0x111], static_source_2, 0x2f);
memcpy(sessionKey, initial_session_key, 16);
for (round = 0; round < 5; round++)
{
unsigned char* base = &newSap[round * 64];
print_block("Input block: ", &base[0]);
print_block("Input block: ", &base[0x10]);
print_block("Input block: ", &base[0x20]);
print_block("Input block: ", &base[0x30]);
modified_md5(base, sessionKey, md5);
printf("MD5 OK\n");
sap_hash(base, sessionKey);
printf("OtherHash OK\n");
printf("MD5 = ");
for (i = 0; i < 4; i++)
printf("%08x ", ((uint32_t*)md5)[i]);
printf("\nOtherHash = ");
for (i = 0; i < 4; i++)
printf("%08x ", ((uint32_t*)sessionKey)[i]);
printf("\n");
uint32_t* sessionKeyWords = (uint32_t*)sessionKey;
uint32_t* md5Words = (uint32_t*)md5;
for (i = 0; i < 4; i++)
{
sessionKeyWords[i] = (sessionKeyWords[i] + md5Words[i]) & 0xffffffff;
}
printf("Current key: ");
for (i = 0; i < 16; i++)
printf("%02x", sessionKey[i]);
printf("\n");
}
for (i = 0; i < 16; i+=4)
{
swap_bytes(&sessionKey[i], &sessionKey[i+3]);
swap_bytes(&sessionKey[i+1], &sessionKey[i+2]);
}
// Finally the whole thing is XORd with 121:
for (i = 0; i < 16; i++)
sessionKey[i] ^= 121;
print_block("Session key computed as: ", sessionKey);
}
unsigned char default_sap[] =
{ 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x53,
0x00, 0x01, 0xcc, 0x34, 0x2a, 0x5e, 0x5b, 0x1a, 0x67, 0x73, 0xc2, 0x0e, 0x21, 0xb8, 0x22, 0x4d,
0xf8, 0x62, 0x48, 0x18, 0x64, 0xef, 0x81, 0x0a, 0xae, 0x2e, 0x37, 0x03, 0xc8, 0x81, 0x9c, 0x23,
0x53, 0x9d, 0xe5, 0xf5, 0xd7, 0x49, 0xbc, 0x5b, 0x7a, 0x26, 0x6c, 0x49, 0x62, 0x83, 0xce, 0x7f,
0x03, 0x93, 0x7a, 0xe1, 0xf6, 0x16, 0xde, 0x0c, 0x15, 0xff, 0x33, 0x8c, 0xca, 0xff, 0xb0, 0x9e,
0xaa, 0xbb, 0xe4, 0x0f, 0x5d, 0x5f, 0x55, 0x8f, 0xb9, 0x7f, 0x17, 0x31, 0xf8, 0xf7, 0xda, 0x60,
0xa0, 0xec, 0x65, 0x79, 0xc3, 0x3e, 0xa9, 0x83, 0x12, 0xc3, 0xb6, 0x71, 0x35, 0xa6, 0x69, 0x4f,
0xf8, 0x23, 0x05, 0xd9, 0xba, 0x5c, 0x61, 0x5f, 0xa2, 0x54, 0xd2, 0xb1, 0x83, 0x45, 0x83, 0xce,
0xe4, 0x2d, 0x44, 0x26, 0xc8, 0x35, 0xa7, 0xa5, 0xf6, 0xc8, 0x42, 0x1c, 0x0d, 0xa3, 0xf1, 0xc7,
0x00, 0x50, 0xf2, 0xe5, 0x17, 0xf8, 0xd0, 0xfa, 0x77, 0x8d, 0xfb, 0x82, 0x8d, 0x40, 0xc7, 0x8e,
0x94, 0x1e, 0x1e, 0x1e};

32
lib/playfair/omg_hax.h Executable file

File diff suppressed because one or more lines are too long

31
lib/playfair/playfair.c Executable file
View File

@@ -0,0 +1,31 @@
#include <stdint.h>
#include "playfair.h"
void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4]);
void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey);
void cycle(unsigned char* block, uint32_t key_schedule[11][4]);
void z_xor(unsigned char* in, unsigned char* out, int blocks);
void x_xor(unsigned char* in, unsigned char* out, int blocks);
extern unsigned char default_sap[];
void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut)
{
unsigned char* chunk1 = &cipherText[16];
unsigned char* chunk2 = &cipherText[56];
int i;
unsigned char blockIn[16];
unsigned char sapKey[16];
uint32_t key_schedule[11][4];
generate_session_key(default_sap, message3, sapKey);
generate_key_schedule(sapKey, key_schedule);
z_xor(chunk2, blockIn, 1);
cycle(blockIn, key_schedule);
for (i = 0; i < 16; i++) {
keyOut[i] = blockIn[i] ^ chunk1[i];
}
x_xor(keyOut, keyOut, 1);
z_xor(keyOut, keyOut, 1);
}

6
lib/playfair/playfair.h Executable file
View File

@@ -0,0 +1,6 @@
#ifndef PLAYFAIR_H
#define PLAYFAIR_H
void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut);
#endif

96
lib/playfair/sap_hash.c Executable file
View File

@@ -0,0 +1,96 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define printf(...) (void)0;
void garble(unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned char*);
unsigned char rol8(unsigned char input, int count)
{
return ((input << count) & 0xff) | (input & 0xff) >> (8-count);
}
uint32_t rol8x(unsigned char input, int count)
{
return ((input << count)) | (input) >> (8-count);
}
void sap_hash(unsigned char* blockIn, unsigned char* keyOut)
{
uint32_t* block_words = (uint32_t*)blockIn;
uint32_t* out_words = (uint32_t*)keyOut;
unsigned char buffer0[20] = {0x96, 0x5F, 0xC6, 0x53, 0xF8, 0x46, 0xCC, 0x18, 0xDF, 0xBE, 0xB2, 0xF8, 0x38, 0xD7, 0xEC, 0x22, 0x03, 0xD1, 0x20, 0x8F};
unsigned char buffer1[210];
unsigned char buffer2[35] = {0x43, 0x54, 0x62, 0x7A, 0x18, 0xC3, 0xD6, 0xB3, 0x9A, 0x56, 0xF6, 0x1C, 0x14, 0x3F, 0x0C, 0x1D, 0x3B, 0x36, 0x83, 0xB1, 0x39, 0x51, 0x4A, 0xAA, 0x09, 0x3E, 0xFE, 0x44, 0xAF, 0xDE, 0xC3, 0x20, 0x9D, 0x42, 0x3A};
unsigned char buffer3[132];
unsigned char buffer4[21] = {0xED, 0x25, 0xD1, 0xBB, 0xBC, 0x27, 0x9F, 0x02, 0xA2, 0xA9, 0x11, 0x00, 0x0C, 0xB3, 0x52, 0xC0, 0xBD, 0xE3, 0x1B, 0x49, 0xC7};
int i0_index[11] = {18, 22, 23, 0, 5, 19, 32, 31, 10, 21, 30};
uint8_t w,x,y,z;
int i, j;
// Load the input into the buffer
for (i = 0; i < 210; i++)
{
// We need to swap the byte order around so it is the right endianness
uint32_t in_word = block_words[((i % 64)>>2)];
uint32_t in_byte = (in_word >> ((3-(i % 4)) << 3)) & 0xff;
buffer1[i] = in_byte;
}
// Next a scrambling
for (i = 0; i < 840; i++)
{
// We have to do unsigned, 32-bit modulo, or we get the wrong indices
x = buffer1[((i-155) & 0xffffffff) % 210];
y = buffer1[((i-57) & 0xffffffff) % 210];
z = buffer1[((i-13) & 0xffffffff) % 210];
w = buffer1[(i & 0xffffffff) % 210];
buffer1[i % 210] = (rol8(y, 5) + (rol8(z, 3) ^ w) - rol8(x,7)) & 0xff;
}
printf("Garbling...\n");
// I have no idea what this is doing (yet), but it gives the right output
garble(buffer0, buffer1, buffer2, buffer3, buffer4);
// Fill the output with 0xE1
for (i = 0; i < 16; i++)
keyOut[i] = 0xE1;
// Now we use all the buffers we have calculated to grind out the output. First buffer3
for (i = 0; i < 11; i++)
{
// Note that this is addition (mod 255) and not XOR
// Also note that we only use certain indices
// And that index 3 is hard-coded to be 0x3d (Maybe we can hack this up by changing buffer3[0] to be 0xdc?
if (i == 3)
keyOut[i] = 0x3d;
else
keyOut[i] = ((keyOut[i] + buffer3[i0_index[i] * 4]) & 0xff);
}
// Then buffer0
for (i = 0; i < 20; i++)
keyOut[i % 16] ^= buffer0[i];
// Then buffer2
for (i = 0; i < 35; i++)
keyOut[i % 16] ^= buffer2[i];
// Do buffer1
for (i = 0; i < 210; i++)
keyOut[(i % 16)] ^= buffer1[i];
// Now we do a kind of reverse-scramble
for (j = 0; j < 16; j++)
{
for (i = 0; i < 16; i++)
{
x = keyOut[((i-7) & 0xffffffff) % 16];
y = keyOut[i % 16];
z = keyOut[((i-37) & 0xffffffff) % 16];
w = keyOut[((i-177) & 0xffffffff) % 16];
keyOut[i] = rol8(x, 1) ^ y ^ rol8(z, 6) ^ rol8(w, 5);
}
}
}

6
lib/plist/CMakeLists.txt Executable file
View File

@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.4.1)
aux_source_directory(. plist_src)
set(DIR_SRCS ${plist_src})
add_library( plist
STATIC
${DIR_SRCS})

119
lib/plist/base64.c Executable file
View File

@@ -0,0 +1,119 @@
/*
* base64.c
* base64 encode/decode implementation
*
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include "base64.h"
static const char base64_str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char base64_pad = '=';
static const signed char base64_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
size_t base64encode(char *outbuf, const unsigned char *buf, size_t size)
{
if (!outbuf || !buf || (size <= 0)) {
return 0;
}
size_t n = 0;
size_t m = 0;
unsigned char input[3];
unsigned int output[4];
while (n < size) {
input[0] = buf[n];
input[1] = (n+1 < size) ? buf[n+1] : 0;
input[2] = (n+2 < size) ? buf[n+2] : 0;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 3) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 15) << 2) + (input[2] >> 6);
output[3] = input[2] & 63;
outbuf[m++] = base64_str[(int)output[0]];
outbuf[m++] = base64_str[(int)output[1]];
outbuf[m++] = (n+1 < size) ? base64_str[(int)output[2]] : base64_pad;
outbuf[m++] = (n+2 < size) ? base64_str[(int)output[3]] : base64_pad;
n+=3;
}
outbuf[m] = 0; // 0-termination!
return m;
}
unsigned char *base64decode(const char *buf, size_t *size)
{
if (!buf || !size) return NULL;
size_t len = (*size > 0) ? *size : strlen(buf);
if (len <= 0) return NULL;
unsigned char *outbuf = (unsigned char*)malloc((len/4)*3+3);
const char *ptr = buf;
int p = 0;
int wv, w1, w2, w3, w4;
int tmpval[4];
int tmpcnt = 0;
do {
while (ptr < buf+len && (*ptr == ' ' || *ptr == '\t' || *ptr == '\n' || *ptr == '\r')) {
ptr++;
}
if (*ptr == '\0' || ptr >= buf+len) {
break;
}
if ((wv = base64_table[(int)(unsigned char)*ptr++]) == -1) {
continue;
}
tmpval[tmpcnt++] = wv;
if (tmpcnt == 4) {
tmpcnt = 0;
w1 = tmpval[0];
w2 = tmpval[1];
w3 = tmpval[2];
w4 = tmpval[3];
if (w1 >= 0 && w2 >= 0) {
outbuf[p++] = (unsigned char)(((w1 << 2) + (w2 >> 4)) & 0xFF);
}
if (w2 >= 0 && w3 >= 0) {
outbuf[p++] = (unsigned char)(((w2 << 4) + (w3 >> 2)) & 0xFF);
}
if (w3 >= 0 && w4 >= 0) {
outbuf[p++] = (unsigned char)(((w3 << 6) + w4) & 0xFF);
}
}
} while (1);
outbuf[p] = 0;
*size = p;
return outbuf;
}

28
lib/plist/base64.h Executable file
View File

@@ -0,0 +1,28 @@
/*
* base64.h
* base64 encode/decode implementation
*
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef BASE64_H
#define BASE64_H
#include <stdlib.h>
size_t base64encode(char *outbuf, const unsigned char *buf, size_t size);
unsigned char *base64decode(const char *buf, size_t *size);
#endif

1381
lib/plist/bplist.c Executable file

File diff suppressed because it is too large Load Diff

61
lib/plist/bytearray.c Executable file
View File

@@ -0,0 +1,61 @@
/*
* bytearray.c
* simple byte array implementation
*
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include "bytearray.h"
#define PAGE_SIZE 4096
bytearray_t *byte_array_new(size_t initial)
{
bytearray_t *a = (bytearray_t*)malloc(sizeof(bytearray_t));
a->capacity = (initial > PAGE_SIZE) ? (initial+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE;
a->data = malloc(a->capacity);
a->len = 0;
return a;
}
void byte_array_free(bytearray_t *ba)
{
if (!ba) return;
if (ba->data) {
free(ba->data);
}
free(ba);
}
void byte_array_grow(bytearray_t *ba, size_t amount)
{
size_t increase = (amount > PAGE_SIZE) ? (amount+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE;
ba->data = realloc(ba->data, ba->capacity + increase);
ba->capacity += increase;
}
void byte_array_append(bytearray_t *ba, void *buf, size_t len)
{
if (!ba || !ba->data || (len <= 0)) return;
size_t remaining = ba->capacity-ba->len;
if (len > remaining) {
size_t needed = len - remaining;
byte_array_grow(ba, needed);
}
memcpy(((char*)ba->data) + ba->len, buf, len);
ba->len += len;
}

36
lib/plist/bytearray.h Executable file
View File

@@ -0,0 +1,36 @@
/*
* bytearray.h
* header file for simple byte array implementation
*
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef BYTEARRAY_H
#define BYTEARRAY_H
#include <stdlib.h>
typedef struct bytearray_t {
void *data;
size_t len;
size_t capacity;
} bytearray_t;
bytearray_t *byte_array_new(size_t initial);
void byte_array_free(bytearray_t *ba);
void byte_array_grow(bytearray_t *ba, size_t amount);
void byte_array_append(bytearray_t *ba, void *buf, size_t len);
#endif

46
lib/plist/cnary.c Executable file
View File

@@ -0,0 +1,46 @@
/*
* cnary.c
*
* Created on: Mar 9, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include "node.h"
int main(int argc, char* argv[]) {
puts("Creating root node");
node_t* root = node_create(NULL, NULL);
puts("Creating child 1 node");
node_t* one = node_create(root, NULL);
puts("Creating child 2 node");
node_t* two = node_create(root, NULL);
puts("Creating child 3 node");
node_t* three = node_create(one, NULL);
puts("Debugging root node");
node_debug(root);
puts("Destroying root node");
node_destroy(root);
return 0;
}

140
lib/plist/hashtable.c Executable file
View File

@@ -0,0 +1,140 @@
/*
* hashtable.c
* really simple hash table implementation
*
* Copyright (c) 2011-2016 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hashtable.h"
hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func)
{
hashtable_t* ht = (hashtable_t*)malloc(sizeof(hashtable_t));
int i;
for (i = 0; i < 4096; i++) {
ht->entries[i] = NULL;
}
ht->count = 0;
ht->hash_func = hash_func;
ht->compare_func = compare_func;
ht->free_func = free_func;
return ht;
}
void hash_table_destroy(hashtable_t *ht)
{
if (!ht) return;
int i = 0;
for (i = 0; i < 4096; i++) {
if (ht->entries[i]) {
hashentry_t* e = ht->entries[i];
while (e) {
if (ht->free_func) {
ht->free_func(e->value);
}
hashentry_t* old = e;
e = e->next;
free(old);
}
}
}
free(ht);
}
void hash_table_insert(hashtable_t* ht, void *key, void *value)
{
if (!ht || !key) return;
unsigned int hash = ht->hash_func(key);
int idx0 = hash & 0xFFF;
// get the idx0 list
hashentry_t* e = ht->entries[idx0];
while (e) {
if (ht->compare_func(e->key, key)) {
// element already present. replace value.
e->value = value;
return;
}
e = e->next;
}
// if we get here, the element is not yet in the list.
// make a new entry.
hashentry_t* entry = (hashentry_t*)malloc(sizeof(hashentry_t));
entry->key = key;
entry->value = value;
if (!ht->entries[idx0]) {
// first entry
entry->next = NULL;
} else {
// add to list
entry->next = ht->entries[idx0];
}
ht->entries[idx0] = entry;
ht->count++;
}
void* hash_table_lookup(hashtable_t* ht, void *key)
{
if (!ht || !key) return NULL;
unsigned int hash = ht->hash_func(key);
int idx0 = hash & 0xFFF;
hashentry_t* e = ht->entries[idx0];
while (e) {
if (ht->compare_func(e->key, key)) {
return e->value;
}
e = e->next;
}
return NULL;
}
void hash_table_remove(hashtable_t* ht, void *key)
{
if (!ht || !key) return;
unsigned int hash = ht->hash_func(key);
int idx0 = hash & 0xFFF;
// get the idx0 list
hashentry_t* e = ht->entries[idx0];
hashentry_t* last = e;
while (e) {
if (ht->compare_func(e->key, key)) {
// found element, remove it from the list
hashentry_t* old = e;
if (e == ht->entries[idx0]) {
ht->entries[idx0] = e->next;
} else {
last->next = e->next;
}
if (ht->free_func) {
ht->free_func(old->value);
}
free(old);
return;
}
last = e;
e = e->next;
}
}

50
lib/plist/hashtable.h Executable file
View File

@@ -0,0 +1,50 @@
/*
* hashtable.h
* header file for really simple hash table implementation
*
* Copyright (c) 2011-2016 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <stdlib.h>
typedef struct hashentry_t {
void *key;
void *value;
void *next;
} hashentry_t;
typedef unsigned int(*hash_func_t)(const void* key);
typedef int (*compare_func_t)(const void *a, const void *b);
typedef void (*free_func_t)(void *ptr);
typedef struct hashtable_t {
hashentry_t *entries[4096];
size_t count;
hash_func_t hash_func;
compare_func_t compare_func;
free_func_t free_func;
} hashtable_t;
hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func);
void hash_table_destroy(hashtable_t *ht);
void hash_table_insert(hashtable_t* ht, void *key, void *value);
void* hash_table_lookup(hashtable_t* ht, void *key);
void hash_table_remove(hashtable_t* ht, void *key);
#endif

47
lib/plist/list.c Executable file
View File

@@ -0,0 +1,47 @@
/*
* list.c
*
* Created on: Mar 8, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
void list_init(list_t* list) {
list->next = NULL;
list->prev = list;
}
void list_destroy(list_t* list) {
if(list) {
free(list);
}
}
int list_add(list_t* list, object_t* object) {
return -1;
}
int list_remove(list_t* list, object_t* object) {
return -1;
}

40
lib/plist/list.h Executable file
View File

@@ -0,0 +1,40 @@
/*
* list.h
*
* Created on: Mar 8, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef LIST_H_
#define LIST_H_
#include "object.h"
typedef struct list_t {
void* next;
void* prev;
} list_t;
void list_init(struct list_t* list);
void list_destroy(struct list_t* list);
int list_add(struct list_t* list, struct object_t* object);
int list_remove(struct list_t* list, struct object_t* object);
#endif /* LIST_H_ */

216
lib/plist/node.c Executable file
View File

@@ -0,0 +1,216 @@
/*
* node.c
*
* Created on: Mar 7, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "node.h"
#include "node_list.h"
void node_destroy(node_t* node) {
if(!node) return;
if (node->children && node->children->count > 0) {
node_t* ch;
while ((ch = node->children->begin)) {
node_list_remove(node->children, ch);
node_destroy(ch);
}
}
node_list_destroy(node->children);
node->children = NULL;
free(node);
}
node_t* node_create(node_t* parent, void* data) {
int error = 0;
node_t* node = (node_t*) malloc(sizeof(node_t));
if(node == NULL) {
return NULL;
}
memset(node, '\0', sizeof(node_t));
node->data = data;
node->next = NULL;
node->prev = NULL;
node->count = 0;
node->parent = NULL;
node->children = NULL;
// Pass NULL to create a root node
if(parent != NULL) {
// This is a child node so attach it to it's parent
error = node_attach(parent, node);
if(error < 0) {
// Unable to attach nodes
printf("ERROR: %d \"Unable to attach nodes\"\n", error);
node_destroy(node);
return NULL;
}
}
return node;
}
int node_attach(node_t* parent, node_t* child) {
if (!parent || !child) return -1;
child->parent = parent;
if(!parent->children) {
parent->children = node_list_create();
}
int res = node_list_add(parent->children, child);
if (res == 0) {
parent->count++;
}
return res;
}
int node_detach(node_t* parent, node_t* child) {
if (!parent || !child) return -1;
int node_index = node_list_remove(parent->children, child);
if (node_index >= 0) {
parent->count--;
}
return node_index;
}
int node_insert(node_t* parent, unsigned int node_index, node_t* child)
{
if (!parent || !child) return -1;
child->parent = parent;
if(!parent->children) {
parent->children = node_list_create();
}
int res = node_list_insert(parent->children, node_index, child);
if (res == 0) {
parent->count++;
}
return res;
}
static void _node_debug(node_t* node, unsigned int depth) {
unsigned int i = 0;
node_t* current = NULL;
for(i = 0; i < depth; i++) {
printf("\t");
}
if(!node->parent) {
printf("ROOT\n");
}
if(!node->children && node->parent) {
printf("LEAF\n");
} else {
if(node->parent) {
printf("NODE\n");
}
for (current = node_first_child(node); current; current = node_next_sibling(current)) {
_node_debug(current, depth+1);
}
}
}
void node_debug(node_t* node)
{
_node_debug(node, 0);
}
unsigned int node_n_children(struct node_t* node)
{
if (!node) return 0;
return node->count;
}
node_t* node_nth_child(struct node_t* node, unsigned int n)
{
if (!node || !node->children || !node->children->begin) return NULL;
unsigned int node_index = 0;
int found = 0;
node_t *ch;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
if (node_index++ == n) {
found = 1;
break;
}
}
if (!found) {
return NULL;
}
return ch;
}
node_t* node_first_child(struct node_t* node)
{
if (!node || !node->children) return NULL;
return node->children->begin;
}
node_t* node_prev_sibling(struct node_t* node)
{
if (!node) return NULL;
return node->prev;
}
node_t* node_next_sibling(struct node_t* node)
{
if (!node) return NULL;
return node->next;
}
int node_child_position(struct node_t* parent, node_t* child)
{
if (!parent || !parent->children || !parent->children->begin || !child) return -1;
int node_index = 0;
int found = 0;
node_t *ch;
for (ch = node_first_child(parent); ch; ch = node_next_sibling(ch)) {
if (ch == child) {
found = 1;
break;
}
node_index++;
}
if (!found) {
return -1;
}
return node_index;
}
node_t* node_copy_deep(node_t* node, copy_func_t copy_func)
{
if (!node) return NULL;
void *data = NULL;
if (copy_func) {
data = copy_func(node->data);
}
node_t* copy = node_create(NULL, data);
node_t* ch;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
node_t* cc = node_copy_deep(ch, copy_func);
node_attach(copy, cc);
}
return copy;
}

65
lib/plist/node.h Executable file
View File

@@ -0,0 +1,65 @@
/*
* node.h
*
* Created on: Mar 7, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NODE_H_
#define NODE_H_
#include "object.h"
#define NODE_TYPE 1;
struct node_list_t;
// This class implements the abstract iterator class
typedef struct node_t {
// Super class
struct node_t* next;
struct node_t* prev;
unsigned int count;
// Local Members
void *data;
struct node_t* parent;
struct node_list_t* children;
} node_t;
void node_destroy(struct node_t* node);
struct node_t* node_create(struct node_t* parent, void* data);
int node_attach(struct node_t* parent, struct node_t* child);
int node_detach(struct node_t* parent, struct node_t* child);
int node_insert(struct node_t* parent, unsigned int index, struct node_t* child);
unsigned int node_n_children(struct node_t* node);
node_t* node_nth_child(struct node_t* node, unsigned int n);
node_t* node_first_child(struct node_t* node);
node_t* node_prev_sibling(struct node_t* node);
node_t* node_next_sibling(struct node_t* node);
int node_child_position(struct node_t* parent, node_t* child);
typedef void* (*copy_func_t)(const void *src);
node_t* node_copy_deep(node_t* node, copy_func_t copy_func);
void node_debug(struct node_t* node);
#endif /* NODE_H_ */

154
lib/plist/node_list.c Executable file
View File

@@ -0,0 +1,154 @@
/*
* node_list.c
*
* Created on: Mar 8, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "node.h"
#include "node_list.h"
void node_list_destroy(node_list_t* list) {
if(list != NULL) {
list_destroy((list_t*) list);
}
}
node_list_t* node_list_create() {
node_list_t* list = (node_list_t*) malloc(sizeof(node_list_t));
if(list == NULL) {
return NULL;
}
memset(list, '\0', sizeof(node_list_t));
// Initialize structure
list_init((list_t*) list);
list->count = 0;
return list;
}
int node_list_add(node_list_t* list, node_t* node) {
if (!list || !node) return -1;
// Find the last element in the list
node_t* last = list->end;
// Setup our new node as the new last element
node->next = NULL;
node->prev = last;
// Set the next element of our old "last" element
if (last) {
// but only if the node list is not empty
last->next = node;
}
// Set the lists prev to the new last element
list->end = node;
// Increment our node count for this list
list->count++;
return 0;
}
int node_list_insert(node_list_t* list, unsigned int node_index, node_t* node) {
if (!list || !node) return -1;
if (node_index >= list->count) {
return node_list_add(list, node);
}
// Get the first element in the list
node_t* cur = list->begin;
unsigned int pos = 0;
node_t* prev = NULL;
if (node_index > 0) {
while (pos < node_index) {
prev = cur;
cur = cur->next;
pos++;
}
}
if (prev) {
// Set previous node
node->prev = prev;
// Set next node of our new node to next node of the previous node
node->next = prev->next;
// Set next node of previous node to our new node
prev->next = node;
} else {
node->prev = NULL;
// get old first element in list
node->next = list->begin;
// set new node as first element in list
list->begin = node;
}
if (node->next == NULL) {
// Set the lists prev to the new last element
list->end = node;
} else {
// set prev of the new next element to our node
node->next->prev = node;
}
// Increment our node count for this list
list->count++;
return 0;
}
int node_list_remove(node_list_t* list, node_t* node) {
if (!list || !node) return -1;
if (list->count == 0) return -1;
int node_index = 0;
node_t* n;
for (n = list->begin; n; n = n->next) {
if (node == n) {
node_t* newnode = node->next;
if (node->prev) {
node->prev->next = newnode;
if (newnode) {
newnode->prev = node->prev;
} else {
// last element in the list
list->end = node->prev;
}
} else {
// we just removed the first element
if (newnode) {
newnode->prev = NULL;
}
list->begin = newnode;
}
list->count--;
return node_index;
}
node_index++;
}
return -1;
}

47
lib/plist/node_list.h Executable file
View File

@@ -0,0 +1,47 @@
/*
* node_list.h
*
* Created on: Mar 8, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef NODE_LIST_H_
#define NODE_LIST_H_
struct node_t;
// This class implements the list_t abstract class
typedef struct node_list_t {
// list_t members
struct node_t* begin;
struct node_t* end;
// node_list_t members
unsigned int count;
} node_list_t;
void node_list_destroy(struct node_list_t* list);
struct node_list_t* node_list_create();
int node_list_add(node_list_t* list, node_t* node);
int node_list_insert(node_list_t* list, unsigned int index, node_t* node);
int node_list_remove(node_list_t* list, node_t* node);
#endif /* NODE_LIST_H_ */

41
lib/plist/object.h Executable file
View File

@@ -0,0 +1,41 @@
/*
* object.h
*
* Created on: Mar 8, 2011
* Author: posixninja
*
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef OBJECT_H_
#define OBJECT_H_
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
typedef struct object_t {
void* value;
unsigned int type;
unsigned int size;
} object_t;
#endif /* OBJECT_H_ */

963
lib/plist/plist.c Executable file
View File

@@ -0,0 +1,963 @@
/*
* plist.c
* Builds plist XML structures
*
* Copyright (c) 2009-2016 Nikias Bassen All Rights Reserved.
* Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
* Copyright (c) 2008 Zach C. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include <assert.h>
#include "plist.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include "node.h"
#include "hashtable.h"
extern void plist_xml_init(void);
extern void plist_xml_deinit(void);
extern void plist_bin_init(void);
extern void plist_bin_deinit(void);
static void internal_plist_init(void)
{
plist_bin_init();
plist_xml_init();
}
static void internal_plist_deinit(void)
{
plist_bin_deinit();
plist_xml_deinit();
}
#ifdef WIN32
typedef volatile struct {
LONG lock;
int state;
} thread_once_t;
static thread_once_t init_once = {0, 0};
static thread_once_t deinit_once = {0, 0};
void thread_once(thread_once_t *once_control, void (*init_routine)(void))
{
while (InterlockedExchange(&(once_control->lock), 1) != 0) {
Sleep(1);
}
if (!once_control->state) {
once_control->state = 1;
init_routine();
}
InterlockedExchange(&(once_control->lock), 0);
}
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
thread_once(&init_once, internal_plist_init);
break;
case DLL_PROCESS_DETACH:
thread_once(&deinit_once, internal_plist_deinit);
break;
default:
break;
}
return 1;
}
#else
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
static pthread_once_t deinit_once = PTHREAD_ONCE_INIT;
static void __attribute__((constructor)) libplist_initialize(void)
{
pthread_once(&init_once, internal_plist_init);
}
static void __attribute__((destructor)) libplist_deinitialize(void)
{
pthread_once(&deinit_once, internal_plist_deinit);
}
#endif
PLIST_API int plist_is_binary(const char *plist_data, uint32_t length)
{
if (length < 8) {
return 0;
}
return (memcmp(plist_data, "bplist00", 8) == 0);
}
PLIST_API void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist)
{
if (length < 8) {
*plist = NULL;
return;
}
if (plist_is_binary(plist_data, length)) {
plist_from_bin(plist_data, length, plist);
} else {
plist_from_xml(plist_data, length, plist);
}
}
plist_t plist_new_node(plist_data_t data)
{
return (plist_t) node_create(NULL, data);
}
plist_data_t plist_get_data(const plist_t node)
{
if (!node)
return NULL;
return ((node_t*)node)->data;
}
plist_data_t plist_new_plist_data(void)
{
plist_data_t data = (plist_data_t) calloc(sizeof(struct plist_data_s), 1);
return data;
}
static unsigned int dict_key_hash(const void *data)
{
plist_data_t keydata = (plist_data_t)data;
unsigned int hash = 5381;
size_t i;
char *str = keydata->strval;
for (i = 0; i < keydata->length; str++, i++) {
hash = ((hash << 5) + hash) + *str;
}
return hash;
}
static int dict_key_compare(const void* a, const void* b)
{
plist_data_t data_a = (plist_data_t)a;
plist_data_t data_b = (plist_data_t)b;
if (data_a->strval == NULL || data_b->strval == NULL) {
return FALSE;
}
if (data_a->length != data_b->length) {
return FALSE;
}
return (strcmp(data_a->strval, data_b->strval) == 0) ? TRUE : FALSE;
}
void plist_free_data(plist_data_t data)
{
if (data)
{
switch (data->type)
{
case PLIST_KEY:
case PLIST_STRING:
free(data->strval);
break;
case PLIST_DATA:
free(data->buff);
break;
case PLIST_DICT:
hash_table_destroy(data->hashtable);
break;
default:
break;
}
free(data);
}
}
static int plist_free_node(node_t* node)
{
plist_data_t data = NULL;
int node_index = node_detach(node->parent, node);
data = plist_get_data(node);
plist_free_data(data);
node->data = NULL;
node_t *ch;
for (ch = node_first_child(node); ch; ) {
node_t *next = node_next_sibling(ch);
plist_free_node(ch);
ch = next;
}
node_destroy(node);
return node_index;
}
PLIST_API plist_t plist_new_dict(void)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_DICT;
return plist_new_node(data);
}
PLIST_API plist_t plist_new_array(void)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_ARRAY;
return plist_new_node(data);
}
//These nodes should not be handled by users
static plist_t plist_new_key(const char *val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_KEY;
data->strval = strdup(val);
data->length = strlen(val);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_string(const char *val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_STRING;
data->strval = strdup(val);
data->length = strlen(val);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_bool(uint8_t val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_BOOLEAN;
data->boolval = val;
data->length = sizeof(uint8_t);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_uint(uint64_t val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_UINT;
data->intval = val;
data->length = sizeof(uint64_t);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_uid(uint64_t val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_UID;
data->intval = val;
data->length = sizeof(uint64_t);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_real(double val)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_REAL;
data->realval = val;
data->length = sizeof(double);
return plist_new_node(data);
}
PLIST_API plist_t plist_new_data(const char *val, uint64_t length)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_DATA;
data->buff = (uint8_t *) malloc(length);
memcpy(data->buff, val, length);
data->length = length;
return plist_new_node(data);
}
PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec)
{
plist_data_t data = plist_new_plist_data();
data->type = PLIST_DATE;
data->realval = (double)sec + (double)usec / 1000000;
data->length = sizeof(double);
return plist_new_node(data);
}
PLIST_API void plist_free(plist_t plist)
{
if (plist)
{
plist_free_node(plist);
}
}
static void plist_copy_node(node_t *node, void *parent_node_ptr)
{
plist_type node_type = PLIST_NONE;
plist_t newnode = NULL;
plist_data_t data = plist_get_data(node);
plist_data_t newdata = plist_new_plist_data();
assert(data); // plist should always have data
memcpy(newdata, data, sizeof(struct plist_data_s));
node_type = plist_get_node_type(node);
switch (node_type) {
case PLIST_DATA:
newdata->buff = (uint8_t *) malloc(data->length);
memcpy(newdata->buff, data->buff, data->length);
break;
case PLIST_KEY:
case PLIST_STRING:
newdata->strval = strdup((char *) data->strval);
break;
case PLIST_DICT:
if (data->hashtable) {
hashtable_t* ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
assert(ht);
plist_t current = NULL;
for (current = (plist_t)node_first_child(node);
ht && current;
current = (plist_t)node_next_sibling(node_next_sibling(current)))
{
hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current));
}
newdata->hashtable = ht;
}
break;
default:
break;
}
newnode = plist_new_node(newdata);
if (*(plist_t*)parent_node_ptr)
{
node_attach(*(plist_t*)parent_node_ptr, newnode);
}
else
{
*(plist_t*)parent_node_ptr = newnode;
}
node_t *ch;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
plist_copy_node(ch, &newnode);
}
}
PLIST_API plist_t plist_copy(plist_t node)
{
plist_t copied = NULL;
plist_copy_node(node, &copied);
return copied;
}
PLIST_API uint32_t plist_array_get_size(plist_t node)
{
uint32_t ret = 0;
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
ret = node_n_children(node);
}
return ret;
}
PLIST_API plist_t plist_array_get_item(plist_t node, uint32_t n)
{
plist_t ret = NULL;
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
ret = (plist_t)node_nth_child(node, n);
}
return ret;
}
PLIST_API uint32_t plist_array_get_item_index(plist_t node)
{
plist_t father = plist_get_parent(node);
if (PLIST_ARRAY == plist_get_node_type(father))
{
return node_child_position(father, node);
}
return 0;
}
PLIST_API void plist_array_set_item(plist_t node, plist_t item, uint32_t n)
{
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
plist_t old_item = plist_array_get_item(node, n);
if (old_item)
{
int idx = plist_free_node(old_item);
if (idx < 0) {
node_attach(node, item);
} else {
node_insert(node, idx, item);
}
}
}
return;
}
PLIST_API void plist_array_append_item(plist_t node, plist_t item)
{
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
node_attach(node, item);
}
return;
}
PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n)
{
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
node_insert(node, n, item);
}
return;
}
PLIST_API void plist_array_remove_item(plist_t node, uint32_t n)
{
if (node && PLIST_ARRAY == plist_get_node_type(node))
{
plist_t old_item = plist_array_get_item(node, n);
if (old_item)
{
plist_free(old_item);
}
}
return;
}
PLIST_API uint32_t plist_dict_get_size(plist_t node)
{
uint32_t ret = 0;
if (node && PLIST_DICT == plist_get_node_type(node))
{
ret = node_n_children(node) / 2;
}
return ret;
}
PLIST_API void plist_dict_new_iter(plist_t node, plist_dict_iter *iter)
{
if (iter && *iter == NULL)
{
*iter = malloc(sizeof(node_t*));
*((node_t**)(*iter)) = node_first_child(node);
}
return;
}
PLIST_API void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val)
{
node_t** iter_node = (node_t**)iter;
if (key)
{
*key = NULL;
}
if (val)
{
*val = NULL;
}
if (node && PLIST_DICT == plist_get_node_type(node) && *iter_node)
{
if (key)
{
plist_get_key_val((plist_t)(*iter_node), key);
}
*iter_node = node_next_sibling(*iter_node);
if (val)
{
*val = (plist_t)(*iter_node);
}
*iter_node = node_next_sibling(*iter_node);
}
return;
}
PLIST_API void plist_dict_get_item_key(plist_t node, char **key)
{
plist_t father = plist_get_parent(node);
if (PLIST_DICT == plist_get_node_type(father))
{
plist_get_key_val( (plist_t) node_prev_sibling(node), key);
}
}
PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key)
{
plist_t ret = NULL;
if (node && PLIST_DICT == plist_get_node_type(node))
{
plist_data_t data = plist_get_data(node);
hashtable_t *ht = (hashtable_t*)data->hashtable;
if (ht) {
struct plist_data_s sdata;
sdata.strval = (char*)key;
sdata.length = strlen(key);
ret = (plist_t)hash_table_lookup(ht, &sdata);
} else {
plist_t current = NULL;
for (current = (plist_t)node_first_child(node);
current;
current = (plist_t)node_next_sibling(node_next_sibling(current)))
{
data = plist_get_data(current);
assert( PLIST_KEY == plist_get_node_type(current) );
if (data && !strcmp(key, data->strval))
{
ret = (plist_t)node_next_sibling(current);
break;
}
}
}
}
return ret;
}
PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item)
{
if (node && PLIST_DICT == plist_get_node_type(node)) {
node_t* old_item = plist_dict_get_item(node, key);
plist_t key_node = NULL;
if (old_item) {
int idx = plist_free_node(old_item);
if (idx < 0) {
node_attach(node, item);
} else {
node_insert(node, idx, item);
}
key_node = node_prev_sibling(item);
} else {
key_node = plist_new_key(key);
node_attach(node, key_node);
node_attach(node, item);
}
hashtable_t *ht = ((plist_data_t)((node_t*)node)->data)->hashtable;
if (ht) {
/* store pointer to item in hash table */
hash_table_insert(ht, (plist_data_t)((node_t*)key_node)->data, item);
} else {
if (((node_t*)node)->count > 500) {
/* make new hash table */
ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
/* calculate the hashes for all entries we have so far */
plist_t current = NULL;
for (current = (plist_t)node_first_child(node);
ht && current;
current = (plist_t)node_next_sibling(node_next_sibling(current)))
{
hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current));
}
((plist_data_t)((node_t*)node)->data)->hashtable = ht;
}
}
}
return;
}
PLIST_API void plist_dict_insert_item(plist_t node, const char* key, plist_t item)
{
plist_dict_set_item(node, key, item);
}
PLIST_API void plist_dict_remove_item(plist_t node, const char* key)
{
if (node && PLIST_DICT == plist_get_node_type(node))
{
plist_t old_item = plist_dict_get_item(node, key);
if (old_item)
{
plist_t key_node = node_prev_sibling(old_item);
hashtable_t* ht = ((plist_data_t)((node_t*)node)->data)->hashtable;
if (ht) {
hash_table_remove(ht, ((node_t*)key_node)->data);
}
plist_free(key_node);
plist_free(old_item);
}
}
return;
}
PLIST_API void plist_dict_merge(plist_t *target, plist_t source)
{
if (!target || !*target || (plist_get_node_type(*target) != PLIST_DICT) || !source || (plist_get_node_type(source) != PLIST_DICT))
return;
char* key = NULL;
plist_dict_iter it = NULL;
plist_t subnode = NULL;
plist_dict_new_iter(source, &it);
if (!it)
return;
do {
plist_dict_next_item(source, it, &key, &subnode);
if (!key)
break;
plist_dict_set_item(*target, key, plist_copy(subnode));
free(key);
key = NULL;
} while (1);
free(it);
}
PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v)
{
plist_t current = plist;
plist_type type = PLIST_NONE;
uint32_t i = 0;
for (i = 0; i < length && current; i++)
{
type = plist_get_node_type(current);
if (type == PLIST_ARRAY)
{
uint32_t n = va_arg(v, uint32_t);
current = plist_array_get_item(current, n);
}
else if (type == PLIST_DICT)
{
const char* key = va_arg(v, const char*);
current = plist_dict_get_item(current, key);
}
}
return current;
}
PLIST_API plist_t plist_access_path(plist_t plist, uint32_t length, ...)
{
plist_t ret = NULL;
va_list v;
va_start(v, length);
ret = plist_access_pathv(plist, length, v);
va_end(v);
return ret;
}
static void plist_get_type_and_value(plist_t node, plist_type * type, void *value, uint64_t * length)
{
plist_data_t data = NULL;
if (!node)
return;
data = plist_get_data(node);
*type = data->type;
*length = data->length;
switch (*type)
{
case PLIST_BOOLEAN:
*((char *) value) = data->boolval;
break;
case PLIST_UINT:
case PLIST_UID:
*((uint64_t *) value) = data->intval;
break;
case PLIST_REAL:
case PLIST_DATE:
*((double *) value) = data->realval;
break;
case PLIST_KEY:
case PLIST_STRING:
*((char **) value) = strdup(data->strval);
break;
case PLIST_DATA:
*((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t));
memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t));
break;
case PLIST_ARRAY:
case PLIST_DICT:
default:
break;
}
}
PLIST_API plist_t plist_get_parent(plist_t node)
{
return node ? (plist_t) ((node_t*) node)->parent : NULL;
}
PLIST_API plist_type plist_get_node_type(plist_t node)
{
if (node)
{
plist_data_t data = plist_get_data(node);
if (data)
return data->type;
}
return PLIST_NONE;
}
PLIST_API void plist_get_key_val(plist_t node, char **val)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_KEY == type)
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == strlen(*val));
}
PLIST_API void plist_get_string_val(plist_t node, char **val)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_STRING == type)
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == strlen(*val));
}
PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_BOOLEAN == type)
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(uint8_t));
}
PLIST_API void plist_get_uint_val(plist_t node, uint64_t * val)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_UINT == type)
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(uint64_t) || length == 16);
}
PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_UID == type)
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(uint64_t));
}
PLIST_API void plist_get_real_val(plist_t node, double *val)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
if (PLIST_REAL == type)
plist_get_type_and_value(node, &type, (void *) val, &length);
assert(length == sizeof(double));
}
PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length)
{
plist_type type = plist_get_node_type(node);
if (PLIST_DATA == type)
plist_get_type_and_value(node, &type, (void *) val, length);
}
PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec)
{
plist_type type = plist_get_node_type(node);
uint64_t length = 0;
double val = 0;
if (PLIST_DATE == type)
plist_get_type_and_value(node, &type, (void *) &val, &length);
assert(length == sizeof(double));
*sec = (int32_t)val;
*usec = (int32_t)fabs((val - (int64_t)val) * 1000000);
}
int plist_data_compare(const void *a, const void *b)
{
plist_data_t val_a = NULL;
plist_data_t val_b = NULL;
if (!a || !b)
return FALSE;
if (!((node_t*) a)->data || !((node_t*) b)->data)
return FALSE;
val_a = plist_get_data((plist_t) a);
val_b = plist_get_data((plist_t) b);
if (val_a->type != val_b->type)
return FALSE;
switch (val_a->type)
{
case PLIST_BOOLEAN:
case PLIST_UINT:
case PLIST_REAL:
case PLIST_DATE:
case PLIST_UID:
if (val_a->length != val_b->length)
return FALSE;
if (val_a->intval == val_b->intval) //it is an union so this is sufficient
return TRUE;
else
return FALSE;
case PLIST_KEY:
case PLIST_STRING:
if (!strcmp(val_a->strval, val_b->strval))
return TRUE;
else
return FALSE;
case PLIST_DATA:
if (val_a->length != val_b->length)
return FALSE;
if (!memcmp(val_a->buff, val_b->buff, val_a->length))
return TRUE;
else
return FALSE;
case PLIST_ARRAY:
case PLIST_DICT:
//compare pointer
if (a == b)
return TRUE;
else
return FALSE;
break;
default:
break;
}
return FALSE;
}
PLIST_API char plist_compare_node_value(plist_t node_l, plist_t node_r)
{
return plist_data_compare(node_l, node_r);
}
static void plist_set_element_val(plist_t node, plist_type type, const void *value, uint64_t length)
{
//free previous allocated buffer
plist_data_t data = plist_get_data(node);
assert(data); // a node should always have data attached
switch (data->type)
{
case PLIST_KEY:
case PLIST_STRING:
free(data->strval);
data->strval = NULL;
break;
case PLIST_DATA:
free(data->buff);
data->buff = NULL;
break;
default:
break;
}
//now handle value
data->type = type;
data->length = length;
switch (type)
{
case PLIST_BOOLEAN:
data->boolval = *((char *) value);
break;
case PLIST_UINT:
case PLIST_UID:
data->intval = *((uint64_t *) value);
break;
case PLIST_REAL:
case PLIST_DATE:
data->realval = *((double *) value);
break;
case PLIST_KEY:
case PLIST_STRING:
data->strval = strdup((char *) value);
break;
case PLIST_DATA:
data->buff = (uint8_t *) malloc(length);
memcpy(data->buff, value, length);
break;
case PLIST_ARRAY:
case PLIST_DICT:
default:
break;
}
}
PLIST_API void plist_set_key_val(plist_t node, const char *val)
{
plist_set_element_val(node, PLIST_KEY, val, strlen(val));
}
PLIST_API void plist_set_string_val(plist_t node, const char *val)
{
plist_set_element_val(node, PLIST_STRING, val, strlen(val));
}
PLIST_API void plist_set_bool_val(plist_t node, uint8_t val)
{
plist_set_element_val(node, PLIST_BOOLEAN, &val, sizeof(uint8_t));
}
PLIST_API void plist_set_uint_val(plist_t node, uint64_t val)
{
plist_set_element_val(node, PLIST_UINT, &val, sizeof(uint64_t));
}
PLIST_API void plist_set_uid_val(plist_t node, uint64_t val)
{
plist_set_element_val(node, PLIST_UID, &val, sizeof(uint64_t));
}
PLIST_API void plist_set_real_val(plist_t node, double val)
{
plist_set_element_val(node, PLIST_REAL, &val, sizeof(double));
}
PLIST_API void plist_set_data_val(plist_t node, const char *val, uint64_t length)
{
plist_set_element_val(node, PLIST_DATA, val, length);
}
PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec)
{
double val = (double)sec + (double)usec / 1000000;
plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval));
}

74
lib/plist/plist.h Executable file
View File

@@ -0,0 +1,74 @@
/*
* plist.h
* contains structures and the like for plists
*
* Copyright (c) 2008 Zach C. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PLIST_H
#define PLIST_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "plist/plist.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#ifdef _MSC_VER
#pragma warning(disable:4996)
#pragma warning(disable:4244)
#endif
#ifdef WIN32
#define PLIST_API __declspec( dllexport )
#else
#ifdef HAVE_FVISIBILITY
#define PLIST_API __attribute__((visibility("default")))
#else
#define PLIST_API
#endif
#endif
struct plist_data_s
{
union
{
char boolval;
uint64_t intval;
double realval;
char *strval;
uint8_t *buff;
void *hashtable;
};
uint64_t length;
plist_type type;
};
typedef struct plist_data_s *plist_data_t;
plist_t plist_new_node(plist_data_t data);
plist_data_t plist_get_data(const plist_t node);
plist_data_t plist_new_plist_data(void);
void plist_free_data(plist_data_t data);
int plist_data_compare(const void *a, const void *b);
#endif

686
lib/plist/plist/plist.h Executable file
View File

@@ -0,0 +1,686 @@
/**
* @file plist/plist.h
* @brief Main include of libplist
* \internal
*
* Copyright (c) 2008 Jonathan Beck All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef LIBPLIST_H
#define LIBPLIST_H
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef _MSC_VER
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#ifdef __llvm__
#if defined(__has_extension)
#if (__has_extension(attribute_deprecated_with_message))
#ifndef PLIST_WARN_DEPRECATED
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x)))
#endif
#else
#ifndef PLIST_WARN_DEPRECATED
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated))
#endif
#endif
#else
#ifndef PLIST_WARN_DEPRECATED
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated))
#endif
#endif
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 5)))
#ifndef PLIST_WARN_DEPRECATED
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x)))
#endif
#elif defined(_MSC_VER)
#ifndef PLIST_WARN_DEPRECATED
#define PLIST_WARN_DEPRECATED(x) __declspec(deprecated(x))
#endif
#else
#define PLIST_WARN_DEPRECATED(x)
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#endif
#include <sys/types.h>
#include <stdarg.h>
/**
* \mainpage libplist : A library to handle Apple Property Lists
* \defgroup PublicAPI Public libplist API
*/
/*@{*/
/**
* The basic plist abstract data type.
*/
typedef void *plist_t;
/**
* The plist dictionary iterator.
*/
typedef void *plist_dict_iter;
/**
* The enumeration of plist node types.
*/
typedef enum
{
PLIST_BOOLEAN, /**< Boolean, scalar type */
PLIST_UINT, /**< Unsigned integer, scalar type */
PLIST_REAL, /**< Real, scalar type */
PLIST_STRING, /**< ASCII string, scalar type */
PLIST_ARRAY, /**< Ordered array, structured type */
PLIST_DICT, /**< Unordered dictionary (key/value pair), structured type */
PLIST_DATE, /**< Date, scalar type */
PLIST_DATA, /**< Binary data, scalar type */
PLIST_KEY, /**< Key in dictionaries (ASCII String), scalar type */
PLIST_UID, /**< Special type used for 'keyed encoding' */
PLIST_NONE /**< No type */
} plist_type;
/********************************************
* *
* Creation & Destruction *
* *
********************************************/
/**
* Create a new root plist_t type #PLIST_DICT
*
* @return the created plist
* @sa #plist_type
*/
plist_t plist_new_dict(void);
/**
* Create a new root plist_t type #PLIST_ARRAY
*
* @return the created plist
* @sa #plist_type
*/
plist_t plist_new_array(void);
/**
* Create a new plist_t type #PLIST_STRING
*
* @param val the sting value, encoded in UTF8.
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_string(const char *val);
/**
* Create a new plist_t type #PLIST_BOOLEAN
*
* @param val the boolean value, 0 is false, other values are true.
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_bool(uint8_t val);
/**
* Create a new plist_t type #PLIST_UINT
*
* @param val the unsigned integer value
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_uint(uint64_t val);
/**
* Create a new plist_t type #PLIST_REAL
*
* @param val the real value
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_real(double val);
/**
* Create a new plist_t type #PLIST_DATA
*
* @param val the binary buffer
* @param length the length of the buffer
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_data(const char *val, uint64_t length);
/**
* Create a new plist_t type #PLIST_DATE
*
* @param sec the number of seconds since 01/01/2001
* @param usec the number of microseconds
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_date(int32_t sec, int32_t usec);
/**
* Create a new plist_t type #PLIST_UID
*
* @param val the unsigned integer value
* @return the created item
* @sa #plist_type
*/
plist_t plist_new_uid(uint64_t val);
/**
* Destruct a plist_t node and all its children recursively
*
* @param plist the plist to free
*/
void plist_free(plist_t plist);
/**
* Return a copy of passed node and it's children
*
* @param node the plist to copy
* @return copied plist
*/
plist_t plist_copy(plist_t node);
/********************************************
* *
* Array functions *
* *
********************************************/
/**
* Get size of a #PLIST_ARRAY node.
*
* @param node the node of type #PLIST_ARRAY
* @return size of the #PLIST_ARRAY node
*/
uint32_t plist_array_get_size(plist_t node);
/**
* Get the nth item in a #PLIST_ARRAY node.
*
* @param node the node of type #PLIST_ARRAY
* @param n the index of the item to get. Range is [0, array_size[
* @return the nth item or NULL if node is not of type #PLIST_ARRAY
*/
plist_t plist_array_get_item(plist_t node, uint32_t n);
/**
* Get the index of an item. item must be a member of a #PLIST_ARRAY node.
*
* @param node the node
* @return the node index
*/
uint32_t plist_array_get_item_index(plist_t node);
/**
* Set the nth item in a #PLIST_ARRAY node.
* The previous item at index n will be freed using #plist_free
*
* @param node the node of type #PLIST_ARRAY
* @param item the new item at index n. The array is responsible for freeing item when it is no longer needed.
* @param n the index of the item to get. Range is [0, array_size[. Assert if n is not in range.
*/
void plist_array_set_item(plist_t node, plist_t item, uint32_t n);
/**
* Append a new item at the end of a #PLIST_ARRAY node.
*
* @param node the node of type #PLIST_ARRAY
* @param item the new item. The array is responsible for freeing item when it is no longer needed.
*/
void plist_array_append_item(plist_t node, plist_t item);
/**
* Insert a new item at position n in a #PLIST_ARRAY node.
*
* @param node the node of type #PLIST_ARRAY
* @param item the new item to insert. The array is responsible for freeing item when it is no longer needed.
* @param n The position at which the node will be stored. Range is [0, array_size[. Assert if n is not in range.
*/
void plist_array_insert_item(plist_t node, plist_t item, uint32_t n);
/**
* Remove an existing position in a #PLIST_ARRAY node.
* Removed position will be freed using #plist_free.
*
* @param node the node of type #PLIST_ARRAY
* @param n The position to remove. Range is [0, array_size[. Assert if n is not in range.
*/
void plist_array_remove_item(plist_t node, uint32_t n);
/********************************************
* *
* Dictionary functions *
* *
********************************************/
/**
* Get size of a #PLIST_DICT node.
*
* @param node the node of type #PLIST_DICT
* @return size of the #PLIST_DICT node
*/
uint32_t plist_dict_get_size(plist_t node);
/**
* Create an iterator of a #PLIST_DICT node.
* The allocated iterator should be freed with the standard free function.
*
* @param node the node of type #PLIST_DICT
* @param iter iterator of the #PLIST_DICT node
*/
void plist_dict_new_iter(plist_t node, plist_dict_iter *iter);
/**
* Increment iterator of a #PLIST_DICT node.
*
* @param node the node of type #PLIST_DICT
* @param iter iterator of the dictionary
* @param key a location to store the key, or NULL. The caller is responsible
* for freeing the the returned string.
* @param val a location to store the value, or NULL. The caller should *not*
* free the returned value.
*/
void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val);
/**
* Get key associated to an item. Item must be member of a dictionary
*
* @param node the node
* @param key a location to store the key. The caller is responsible for freeing the returned string.
*/
void plist_dict_get_item_key(plist_t node, char **key);
/**
* Get the nth item in a #PLIST_DICT node.
*
* @param node the node of type #PLIST_DICT
* @param key the identifier of the item to get.
* @return the item or NULL if node is not of type #PLIST_DICT. The caller should not free
* the returned node.
*/
plist_t plist_dict_get_item(plist_t node, const char* key);
/**
* Set item identified by key in a #PLIST_DICT node.
* The previous item identified by key will be freed using #plist_free.
* If there is no item for the given key a new item will be inserted.
*
* @param node the node of type #PLIST_DICT
* @param item the new item associated to key
* @param key the identifier of the item to set.
*/
void plist_dict_set_item(plist_t node, const char* key, plist_t item);
/**
* Insert a new item into a #PLIST_DICT node.
*
* @deprecated Deprecated. Use plist_dict_set_item instead.
*
* @param node the node of type #PLIST_DICT
* @param item the new item to insert
* @param key The identifier of the item to insert.
*/
PLIST_WARN_DEPRECATED("use plist_dict_set_item instead")
void plist_dict_insert_item(plist_t node, const char* key, plist_t item);
/**
* Remove an existing position in a #PLIST_DICT node.
* Removed position will be freed using #plist_free
*
* @param node the node of type #PLIST_DICT
* @param key The identifier of the item to remove. Assert if identifier is not present.
*/
void plist_dict_remove_item(plist_t node, const char* key);
/**
* Merge a dictionary into another. This will add all key/value pairs
* from the source dictionary to the target dictionary, overwriting
* any existing key/value pairs that are already present in target.
*
* @param target pointer to an existing node of type #PLIST_DICT
* @param source node of type #PLIST_DICT that should be merged into target
*/
void plist_dict_merge(plist_t *target, plist_t source);
/********************************************
* *
* Getters *
* *
********************************************/
/**
* Get the parent of a node
*
* @param node the parent (NULL if node is root)
*/
plist_t plist_get_parent(plist_t node);
/**
* Get the #plist_type of a node.
*
* @param node the node
* @return the type of the node
*/
plist_type plist_get_node_type(plist_t node);
/**
* Get the value of a #PLIST_KEY node.
* This function does nothing if node is not of type #PLIST_KEY
*
* @param node the node
* @param val a pointer to a C-string. This function allocates the memory,
* caller is responsible for freeing it.
*/
void plist_get_key_val(plist_t node, char **val);
/**
* Get the value of a #PLIST_STRING node.
* This function does nothing if node is not of type #PLIST_STRING
*
* @param node the node
* @param val a pointer to a C-string. This function allocates the memory,
* caller is responsible for freeing it. Data is UTF-8 encoded.
*/
void plist_get_string_val(plist_t node, char **val);
/**
* Get the value of a #PLIST_BOOLEAN node.
* This function does nothing if node is not of type #PLIST_BOOLEAN
*
* @param node the node
* @param val a pointer to a uint8_t variable.
*/
void plist_get_bool_val(plist_t node, uint8_t * val);
/**
* Get the value of a #PLIST_UINT node.
* This function does nothing if node is not of type #PLIST_UINT
*
* @param node the node
* @param val a pointer to a uint64_t variable.
*/
void plist_get_uint_val(plist_t node, uint64_t * val);
/**
* Get the value of a #PLIST_REAL node.
* This function does nothing if node is not of type #PLIST_REAL
*
* @param node the node
* @param val a pointer to a double variable.
*/
void plist_get_real_val(plist_t node, double *val);
/**
* Get the value of a #PLIST_DATA node.
* This function does nothing if node is not of type #PLIST_DATA
*
* @param node the node
* @param val a pointer to an unallocated char buffer. This function allocates the memory,
* caller is responsible for freeing it.
* @param length the length of the buffer
*/
void plist_get_data_val(plist_t node, char **val, uint64_t * length);
/**
* Get the value of a #PLIST_DATE node.
* This function does nothing if node is not of type #PLIST_DATE
*
* @param node the node
* @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001.
* @param usec a pointer to an int32_t variable. Represents the number of microseconds
*/
void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec);
/**
* Get the value of a #PLIST_UID node.
* This function does nothing if node is not of type #PLIST_UID
*
* @param node the node
* @param val a pointer to a uint64_t variable.
*/
void plist_get_uid_val(plist_t node, uint64_t * val);
/********************************************
* *
* Setters *
* *
********************************************/
/**
* Set the value of a node.
* Forces type of node to #PLIST_KEY
*
* @param node the node
* @param val the key value
*/
void plist_set_key_val(plist_t node, const char *val);
/**
* Set the value of a node.
* Forces type of node to #PLIST_STRING
*
* @param node the node
* @param val the string value. The string is copied when set and will be
* freed by the node.
*/
void plist_set_string_val(plist_t node, const char *val);
/**
* Set the value of a node.
* Forces type of node to #PLIST_BOOLEAN
*
* @param node the node
* @param val the boolean value
*/
void plist_set_bool_val(plist_t node, uint8_t val);
/**
* Set the value of a node.
* Forces type of node to #PLIST_UINT
*
* @param node the node
* @param val the unsigned integer value
*/
void plist_set_uint_val(plist_t node, uint64_t val);
/**
* Set the value of a node.
* Forces type of node to #PLIST_REAL
*
* @param node the node
* @param val the real value
*/
void plist_set_real_val(plist_t node, double val);
/**
* Set the value of a node.
* Forces type of node to #PLIST_DATA
*
* @param node the node
* @param val the binary buffer. The buffer is copied when set and will
* be freed by the node.
* @param length the length of the buffer
*/
void plist_set_data_val(plist_t node, const char *val, uint64_t length);
/**
* Set the value of a node.
* Forces type of node to #PLIST_DATE
*
* @param node the node
* @param sec the number of seconds since 01/01/2001
* @param usec the number of microseconds
*/
void plist_set_date_val(plist_t node, int32_t sec, int32_t usec);
/**
* Set the value of a node.
* Forces type of node to #PLIST_UID
*
* @param node the node
* @param val the unsigned integer value
*/
void plist_set_uid_val(plist_t node, uint64_t val);
/********************************************
* *
* Import & Export *
* *
********************************************/
/**
* Export the #plist_t structure to XML format.
*
* @param plist the root node to export
* @param plist_xml a pointer to a C-string. This function allocates the memory,
* caller is responsible for freeing it. Data is UTF-8 encoded.
* @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer.
*/
void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length);
/**
* Export the #plist_t structure to binary format.
*
* @param plist the root node to export
* @param plist_bin a pointer to a char* buffer. This function allocates the memory,
* caller is responsible for freeing it.
* @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer.
*/
void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length);
/**
* Import the #plist_t structure from XML format.
*
* @param plist_xml a pointer to the xml buffer.
* @param length length of the buffer to read.
* @param plist a pointer to the imported plist.
*/
void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist);
/**
* Import the #plist_t structure from binary format.
*
* @param plist_bin a pointer to the xml buffer.
* @param length length of the buffer to read.
* @param plist a pointer to the imported plist.
*/
void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist);
/**
* Import the #plist_t structure from memory data.
* This method will look at the first bytes of plist_data
* to determine if plist_data contains a binary or XML plist.
*
* @param plist_data a pointer to the memory buffer containing plist data.
* @param length length of the buffer to read.
* @param plist a pointer to the imported plist.
*/
void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist);
/**
* Test if in-memory plist data is binary or XML
* This method will look at the first bytes of plist_data
* to determine if plist_data contains a binary or XML plist.
* This method is not validating the whole memory buffer to check if the
* content is truly a plist, it's only using some heuristic on the first few
* bytes of plist_data.
*
* @param plist_data a pointer to the memory buffer containing plist data.
* @param length length of the buffer to read.
* @return 1 if the buffer is a binary plist, 0 otherwise.
*/
int plist_is_binary(const char *plist_data, uint32_t length);
/********************************************
* *
* Utils *
* *
********************************************/
/**
* Get a node from its path. Each path element depends on the associated father node type.
* For Dictionaries, var args are casted to const char*, for arrays, var args are caster to uint32_t
* Search is breath first order.
*
* @param plist the node to access result from.
* @param length length of the path to access
* @return the value to access.
*/
plist_t plist_access_path(plist_t plist, uint32_t length, ...);
/**
* Variadic version of #plist_access_path.
*
* @param plist the node to access result from.
* @param length length of the path to access
* @param v list of array's index and dic'st key
* @return the value to access.
*/
plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v);
/**
* Compare two node values
*
* @param node_l left node to compare
* @param node_r rigth node to compare
* @return TRUE is type and value match, FALSE otherwise.
*/
char plist_compare_node_value(plist_t node_l, plist_t node_r);
#define _PLIST_IS_TYPE(__plist, __plist_type) (__plist && (plist_get_node_type(__plist) == PLIST_##__plist_type))
/* Helper macros for the different plist types */
#define PLIST_IS_BOOLEAN(__plist) _PLIST_IS_TYPE(__plist, BOOLEAN)
#define PLIST_IS_UINT(__plist) _PLIST_IS_TYPE(__plist, UINT)
#define PLIST_IS_REAL(__plist) _PLIST_IS_TYPE(__plist, REAL)
#define PLIST_IS_STRING(__plist) _PLIST_IS_TYPE(__plist, STRING)
#define PLIST_IS_ARRAY(__plist) _PLIST_IS_TYPE(__plist, ARRAY)
#define PLIST_IS_DICT(__plist) _PLIST_IS_TYPE(__plist, DICT)
#define PLIST_IS_DATE(__plist) _PLIST_IS_TYPE(__plist, DATE)
#define PLIST_IS_DATA(__plist) _PLIST_IS_TYPE(__plist, DATA)
#define PLIST_IS_KEY(__plist) _PLIST_IS_TYPE(__plist, KEY)
#define PLIST_IS_UID(__plist) _PLIST_IS_TYPE(__plist, UID)
/*@}*/
#ifdef __cplusplus
}
#endif
#endif

61
lib/plist/ptrarray.c Executable file
View File

@@ -0,0 +1,61 @@
/*
* ptrarray.c
* simple pointer array implementation
*
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ptrarray.h"
ptrarray_t *ptr_array_new(int capacity)
{
ptrarray_t *pa = (ptrarray_t*)malloc(sizeof(ptrarray_t));
pa->pdata = (void**)malloc(sizeof(void*) * capacity);
pa->capacity = capacity;
pa->capacity_step = (capacity > 4096) ? 4096 : capacity;
pa->len = 0;
return pa;
}
void ptr_array_free(ptrarray_t *pa)
{
if (!pa) return;
if (pa->pdata) {
free(pa->pdata);
}
free(pa);
}
void ptr_array_add(ptrarray_t *pa, void *data)
{
if (!pa || !pa->pdata || !data) return;
size_t remaining = pa->capacity-pa->len;
if (remaining == 0) {
pa->pdata = realloc(pa->pdata, sizeof(void*) * (pa->capacity + pa->capacity_step));
pa->capacity += pa->capacity_step;
}
pa->pdata[pa->len] = data;
pa->len++;
}
void* ptr_array_index(ptrarray_t *pa, size_t array_index)
{
if (!pa) return NULL;
if (array_index >= pa->len) {
return NULL;
}
return pa->pdata[array_index];
}

36
lib/plist/ptrarray.h Executable file
View File

@@ -0,0 +1,36 @@
/*
* ptrarray.h
* header file for simple pointer array implementation
*
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PTRARRAY_H
#define PTRARRAY_H
#include <stdlib.h>
typedef struct ptrarray_t {
void **pdata;
size_t len;
size_t capacity;
size_t capacity_step;
} ptrarray_t;
ptrarray_t *ptr_array_new(int capacity);
void ptr_array_free(ptrarray_t *pa);
void ptr_array_add(ptrarray_t *pa, void *data);
void* ptr_array_index(ptrarray_t *pa, size_t index);
#endif

34
lib/plist/strbuf.h Executable file
View File

@@ -0,0 +1,34 @@
/*
* strbuf.h
* header file for simple string buffer, using the bytearray as underlying
* structure.
*
* Copyright (c) 2016 Nikias Bassen, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STRBUF_H
#define STRBUF_H
#include <stdlib.h>
#include "bytearray.h"
typedef struct bytearray_t strbuf_t;
#define str_buf_new(__sz) byte_array_new(__sz)
#define str_buf_free(__ba) byte_array_free(__ba)
#define str_buf_grow(__ba, __am) byte_array_grow(__ba, __am)
#define str_buf_append(__ba, __str, __len) byte_array_append(__ba, (void*)(__str), __len)
#endif

812
lib/plist/time64.c Executable file
View File

@@ -0,0 +1,812 @@
/*
Copyright (c) 2007-2010 Michael G Schwern
This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
The MIT License:
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.
*/
/*
Programmers who have available to them 64-bit time values as a 'long
long' type can use localtime64_r() and gmtime64_r() which correctly
converts the time even on 32-bit systems. Whether you have 64-bit time
values will depend on the operating system.
localtime64_r() is a 64-bit equivalent of localtime_r().
gmtime64_r() is a 64-bit equivalent of gmtime_r().
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "time64.h"
#include "time64_limits.h"
static const char days_in_month[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
};
static const short julian_days_by_month[2][12] = {
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};
static char wday_name[7][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static char mon_name[12][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static const short length_of_year[2] = { 365, 366 };
/* Some numbers relating to the gregorian cycle */
static const Year years_in_gregorian_cycle = 400;
#define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1)
static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL;
/* Year range we can trust the time funcitons with */
#define MAX_SAFE_YEAR 2037
#define MIN_SAFE_YEAR 1971
/* 28 year Julian calendar cycle */
#define SOLAR_CYCLE_LENGTH 28
/* Year cycle from MAX_SAFE_YEAR down. */
static const short safe_years_high[SOLAR_CYCLE_LENGTH] = {
2016, 2017, 2018, 2019,
2020, 2021, 2022, 2023,
2024, 2025, 2026, 2027,
2028, 2029, 2030, 2031,
2032, 2033, 2034, 2035,
2036, 2037, 2010, 2011,
2012, 2013, 2014, 2015
};
/* Year cycle from MIN_SAFE_YEAR up */
static const int safe_years_low[SOLAR_CYCLE_LENGTH] = {
1996, 1997, 1998, 1971,
1972, 1973, 1974, 1975,
1976, 1977, 1978, 1979,
1980, 1981, 1982, 1983,
1984, 1985, 1986, 1987,
1988, 1989, 1990, 1991,
1992, 1993, 1994, 1995,
};
/* This isn't used, but it's handy to look at */
#if 0
static const char dow_year_start[SOLAR_CYCLE_LENGTH] = {
5, 0, 1, 2, /* 0 2016 - 2019 */
3, 5, 6, 0, /* 4 */
1, 3, 4, 5, /* 8 1996 - 1998, 1971*/
6, 1, 2, 3, /* 12 1972 - 1975 */
4, 6, 0, 1, /* 16 */
2, 4, 5, 6, /* 20 2036, 2037, 2010, 2011 */
0, 2, 3, 4 /* 24 2012, 2013, 2014, 2015 */
};
#endif
/* Let's assume people are going to be looking for dates in the future.
Let's provide some cheats so you can skip ahead.
This has a 4x speed boost when near 2008.
*/
/* Number of days since epoch on Jan 1st, 2008 GMT */
#define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
#define CHEAT_YEARS 108
#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
#ifdef USE_SYSTEM_LOCALTIME
# define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
(a) <= SYSTEM_LOCALTIME_MAX && \
(a) >= SYSTEM_LOCALTIME_MIN \
)
#else
# define SHOULD_USE_SYSTEM_LOCALTIME(a) (0)
#endif
#ifdef USE_SYSTEM_GMTIME
# define SHOULD_USE_SYSTEM_GMTIME(a) ( \
(a) <= SYSTEM_GMTIME_MAX && \
(a) >= SYSTEM_GMTIME_MIN \
)
#else
# define SHOULD_USE_SYSTEM_GMTIME(a) (0)
#endif
/* Multi varadic macros are a C99 thing, alas */
#ifdef TIME_64_DEBUG
# define TIME64_TRACE(format) (fprintf(stderr, format))
# define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1))
# define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
# define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
#else
# define TIME64_TRACE(format) ((void)0)
# define TIME64_TRACE1(format, var1) ((void)0)
# define TIME64_TRACE2(format, var1, var2) ((void)0)
# define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
#endif
static int is_exception_century(Year year)
{
int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
return(is_exception);
}
/* Compare two dates.
The result is like cmp.
Ignores things like gmtoffset and dst
*/
static int cmp_date( const struct TM* left, const struct tm* right ) {
if( left->tm_year > right->tm_year )
return 1;
else if( left->tm_year < right->tm_year )
return -1;
if( left->tm_mon > right->tm_mon )
return 1;
else if( left->tm_mon < right->tm_mon )
return -1;
if( left->tm_mday > right->tm_mday )
return 1;
else if( left->tm_mday < right->tm_mday )
return -1;
if( left->tm_hour > right->tm_hour )
return 1;
else if( left->tm_hour < right->tm_hour )
return -1;
if( left->tm_min > right->tm_min )
return 1;
else if( left->tm_min < right->tm_min )
return -1;
if( left->tm_sec > right->tm_sec )
return 1;
else if( left->tm_sec < right->tm_sec )
return -1;
return 0;
}
/* Check if a date is safely inside a range.
The intention is to check if its a few days inside.
*/
static int date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) {
if( cmp_date(date, min) == -1 )
return 0;
if( cmp_date(date, max) == 1 )
return 0;
return 1;
}
/* timegm() is not in the C or POSIX spec, but it is such a useful
extension I would be remiss in leaving it out. Also I need it
for localtime64()
*/
Time64_T timegm64(const struct TM *date) {
Time64_T days = 0;
Time64_T seconds = 0;
Year year;
Year orig_year = (Year)date->tm_year;
int cycles = 0;
if( orig_year > 100 ) {
cycles = (orig_year - 100) / 400;
orig_year -= cycles * 400;
days += (Time64_T)cycles * days_in_gregorian_cycle;
}
else if( orig_year < -300 ) {
cycles = (orig_year - 100) / 400;
orig_year -= cycles * 400;
days += (Time64_T)cycles * days_in_gregorian_cycle;
}
TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
if( orig_year > 70 ) {
year = 70;
while( year < orig_year ) {
days += length_of_year[IS_LEAP(year)];
year++;
}
}
else if ( orig_year < 70 ) {
year = 69;
do {
days -= length_of_year[IS_LEAP(year)];
year--;
} while( year >= orig_year );
}
days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
days += date->tm_mday - 1;
seconds = days * 60 * 60 * 24;
seconds += date->tm_hour * 60 * 60;
seconds += date->tm_min * 60;
seconds += date->tm_sec;
return(seconds);
}
static int check_tm(struct TM *tm)
{
/* Don't forget leap seconds */
assert(tm->tm_sec >= 0);
assert(tm->tm_sec <= 61);
assert(tm->tm_min >= 0);
assert(tm->tm_min <= 59);
assert(tm->tm_hour >= 0);
assert(tm->tm_hour <= 23);
assert(tm->tm_mday >= 1);
assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
assert(tm->tm_mon >= 0);
assert(tm->tm_mon <= 11);
assert(tm->tm_wday >= 0);
assert(tm->tm_wday <= 6);
assert(tm->tm_yday >= 0);
assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
#ifdef HAVE_TM_TM_GMTOFF
assert(tm->tm_gmtoff >= -24 * 60 * 60);
assert(tm->tm_gmtoff <= 24 * 60 * 60);
#endif
return 1;
}
/* The exceptional centuries without leap years cause the cycle to
shift by 16
*/
static Year cycle_offset(Year year)
{
const Year start_year = 2000;
Year year_diff = year - start_year;
Year exceptions;
if( year > start_year )
year_diff--;
exceptions = year_diff / 100;
exceptions -= year_diff / 400;
TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
year, exceptions, year_diff);
return exceptions * 16;
}
/* For a given year after 2038, pick the latest possible matching
year in the 28 year calendar cycle.
A matching year...
1) Starts on the same day of the week.
2) Has the same leap year status.
This is so the calendars match up.
Also the previous year must match. When doing Jan 1st you might
wind up on Dec 31st the previous year when doing a -UTC time zone.
Finally, the next year must have the same start day of week. This
is for Dec 31st with a +UTC time zone.
It doesn't need the same leap year status since we only care about
January 1st.
*/
static int safe_year(const Year year)
{
int safe_year= (int)year;
Year year_cycle;
if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) {
return safe_year;
}
year_cycle = year + cycle_offset(year);
/* safe_years_low is off from safe_years_high by 8 years */
if( year < MIN_SAFE_YEAR )
year_cycle -= 8;
/* Change non-leap xx00 years to an equivalent */
if( is_exception_century(year) )
year_cycle += 11;
/* Also xx01 years, since the previous year will be wrong */
if( is_exception_century(year - 1) )
year_cycle += 17;
year_cycle %= SOLAR_CYCLE_LENGTH;
if( year_cycle < 0 )
year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
assert( year_cycle >= 0 );
assert( year_cycle < SOLAR_CYCLE_LENGTH );
if( year < MIN_SAFE_YEAR )
safe_year = safe_years_low[year_cycle];
else if( year > MAX_SAFE_YEAR )
safe_year = safe_years_high[year_cycle];
else
assert(0);
TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
year, year_cycle, safe_year);
assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR);
return safe_year;
}
void copy_tm_to_TM64(const struct tm *src, struct TM *dest) {
if( src == NULL ) {
memset(dest, 0, sizeof(*dest));
}
else {
# ifdef USE_TM64
dest->tm_sec = src->tm_sec;
dest->tm_min = src->tm_min;
dest->tm_hour = src->tm_hour;
dest->tm_mday = src->tm_mday;
dest->tm_mon = src->tm_mon;
dest->tm_year = (Year)src->tm_year;
dest->tm_wday = src->tm_wday;
dest->tm_yday = src->tm_yday;
dest->tm_isdst = src->tm_isdst;
# ifdef HAVE_TM_TM_GMTOFF
dest->tm_gmtoff = src->tm_gmtoff;
# endif
# ifdef HAVE_TM_TM_ZONE
dest->tm_zone = src->tm_zone;
# endif
# else
/* They're the same type */
memcpy(dest, src, sizeof(*dest));
# endif
}
}
void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
if( src == NULL ) {
memset(dest, 0, sizeof(*dest));
}
else {
# ifdef USE_TM64
dest->tm_sec = src->tm_sec;
dest->tm_min = src->tm_min;
dest->tm_hour = src->tm_hour;
dest->tm_mday = src->tm_mday;
dest->tm_mon = src->tm_mon;
dest->tm_year = (int)src->tm_year;
dest->tm_wday = src->tm_wday;
dest->tm_yday = src->tm_yday;
dest->tm_isdst = src->tm_isdst;
# ifdef HAVE_TM_TM_GMTOFF
dest->tm_gmtoff = src->tm_gmtoff;
# endif
# ifdef HAVE_TM_TM_ZONE
dest->tm_zone = src->tm_zone;
# endif
# else
/* They're the same type */
memcpy(dest, src, sizeof(*dest));
# endif
}
}
#ifndef HAVE_LOCALTIME_R
/* Simulate localtime_r() to the best of our ability */
static struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
const struct tm *static_result = localtime(time);
assert(result != NULL);
if( static_result == NULL ) {
memset(result, 0, sizeof(*result));
return NULL;
}
else {
memcpy(result, static_result, sizeof(*result));
return result;
}
}
#endif
#ifndef HAVE_GMTIME_R
/* Simulate gmtime_r() to the best of our ability */
static struct tm * fake_gmtime_r(const time_t *time, struct tm *result) {
const struct tm *static_result = gmtime(time);
assert(result != NULL);
if( static_result == NULL ) {
memset(result, 0, sizeof(*result));
return NULL;
}
else {
memcpy(result, static_result, sizeof(*result));
return result;
}
}
#endif
static Time64_T seconds_between_years(Year left_year, Year right_year) {
int increment = (left_year > right_year) ? 1 : -1;
Time64_T seconds = 0;
int cycles;
if( left_year > 2400 ) {
cycles = (left_year - 2400) / 400;
left_year -= cycles * 400;
seconds += cycles * seconds_in_gregorian_cycle;
}
else if( left_year < 1600 ) {
cycles = (left_year - 1600) / 400;
left_year += cycles * 400;
seconds += cycles * seconds_in_gregorian_cycle;
}
while( left_year != right_year ) {
seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24;
right_year += increment;
}
return seconds * increment;
}
Time64_T mktime64(struct TM *input_date) {
struct tm safe_date;
struct TM date;
Time64_T time;
Year year = input_date->tm_year + 1900;
if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) )
{
copy_TM64_to_tm(input_date, &safe_date);
time = (Time64_T)mktime(&safe_date);
/* Correct the possibly out of bound input date */
copy_tm_to_TM64(&safe_date, input_date);
return time;
}
/* Have to make the year safe in date else it won't fit in safe_date */
date = *input_date;
date.tm_year = safe_year(year) - 1900;
copy_TM64_to_tm(&date, &safe_date);
time = (Time64_T)mktime(&safe_date);
/* Correct the user's possibly out of bound input date */
copy_tm_to_TM64(&safe_date, input_date);
time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900));
return time;
}
/* Because I think mktime() is a crappy name */
Time64_T timelocal64(struct TM *date) {
return mktime64(date);
}
struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
{
int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
Time64_T v_tm_tday;
int leap;
Time64_T m;
Time64_T time = *in_time;
Year year = 70;
int cycles = 0;
assert(p != NULL);
/* Use the system gmtime() if time_t is small enough */
if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
time_t safe_time = (time_t)*in_time;
struct tm safe_date;
GMTIME_R(&safe_time, &safe_date);
copy_tm_to_TM64(&safe_date, p);
assert(check_tm(p));
return p;
}
#ifdef HAVE_TM_TM_GMTOFF
p->tm_gmtoff = 0;
#endif
p->tm_isdst = 0;
#ifdef HAVE_TM_TM_ZONE
p->tm_zone = (char*)"UTC";
#endif
v_tm_sec = (int)(time % 60);
time /= 60;
v_tm_min = (int)(time % 60);
time /= 60;
v_tm_hour = (int)(time % 24);
time /= 24;
v_tm_tday = time;
WRAP (v_tm_sec, v_tm_min, 60);
WRAP (v_tm_min, v_tm_hour, 60);
WRAP (v_tm_hour, v_tm_tday, 24);
v_tm_wday = (int)((v_tm_tday + 4) % 7);
if (v_tm_wday < 0)
v_tm_wday += 7;
m = v_tm_tday;
if (m >= CHEAT_DAYS) {
year = CHEAT_YEARS;
m -= CHEAT_DAYS;
}
if (m >= 0) {
/* Gregorian cycles, this is huge optimization for distant times */
cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
if( cycles ) {
m -= (cycles * (Time64_T) days_in_gregorian_cycle);
year += (cycles * years_in_gregorian_cycle);
}
/* Years */
leap = IS_LEAP (year);
while (m >= (Time64_T) length_of_year[leap]) {
m -= (Time64_T) length_of_year[leap];
year++;
leap = IS_LEAP (year);
}
/* Months */
v_tm_mon = 0;
while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
m -= (Time64_T) days_in_month[leap][v_tm_mon];
v_tm_mon++;
}
} else {
year--;
/* Gregorian cycles */
cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
if( cycles ) {
m -= (cycles * (Time64_T) days_in_gregorian_cycle);
year += (cycles * years_in_gregorian_cycle);
}
/* Years */
leap = IS_LEAP (year);
while (m < (Time64_T) -length_of_year[leap]) {
m += (Time64_T) length_of_year[leap];
year--;
leap = IS_LEAP (year);
}
/* Months */
v_tm_mon = 11;
while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
m += (Time64_T) days_in_month[leap][v_tm_mon];
v_tm_mon--;
}
m += (Time64_T) days_in_month[leap][v_tm_mon];
}
p->tm_year = year;
if( p->tm_year != year ) {
#ifdef EOVERFLOW
errno = EOVERFLOW;
#endif
return NULL;
}
/* At this point m is less than a year so casting to an int is safe */
p->tm_mday = (int) m + 1;
p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
p->tm_sec = v_tm_sec;
p->tm_min = v_tm_min;
p->tm_hour = v_tm_hour;
p->tm_mon = v_tm_mon;
p->tm_wday = v_tm_wday;
assert(check_tm(p));
return p;
}
struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
{
time_t safe_time;
struct tm safe_date;
struct TM gm_tm;
Year orig_year;
int month_diff;
assert(local_tm != NULL);
/* Use the system localtime() if time_t is small enough */
if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
safe_time = (time_t)*time;
TIME64_TRACE1("Using system localtime for %lld\n", *time);
LOCALTIME_R(&safe_time, &safe_date);
copy_tm_to_TM64(&safe_date, local_tm);
assert(check_tm(local_tm));
return local_tm;
}
if( gmtime64_r(time, &gm_tm) == NULL ) {
TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
return NULL;
}
orig_year = gm_tm.tm_year;
if (gm_tm.tm_year > (2037 - 1900) ||
gm_tm.tm_year < (1970 - 1900)
)
{
TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
}
safe_time = (time_t)timegm64(&gm_tm);
if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
return NULL;
}
copy_tm_to_TM64(&safe_date, local_tm);
local_tm->tm_year = orig_year;
if( local_tm->tm_year != orig_year ) {
TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
(Year)local_tm->tm_year, (Year)orig_year);
#ifdef EOVERFLOW
errno = EOVERFLOW;
#endif
return NULL;
}
month_diff = local_tm->tm_mon - gm_tm.tm_mon;
/* When localtime is Dec 31st previous year and
gmtime is Jan 1st next year.
*/
if( month_diff == 11 ) {
local_tm->tm_year--;
}
/* When localtime is Jan 1st, next year and
gmtime is Dec 31st, previous year.
*/
if( month_diff == -11 ) {
local_tm->tm_year++;
}
/* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
in a non-leap xx00. There is one point in the cycle
we can't account for which the safe xx00 year is a leap
year. So we need to correct for Dec 31st comming out as
the 366th day of the year.
*/
if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
local_tm->tm_yday--;
assert(check_tm(local_tm));
return local_tm;
}
static int valid_tm_wday( const struct TM* date ) {
if( 0 <= date->tm_wday && date->tm_wday <= 6 )
return 1;
else
return 0;
}
static int valid_tm_mon( const struct TM* date ) {
if( 0 <= date->tm_mon && date->tm_mon <= 11 )
return 1;
else
return 0;
}
char *asctime64_r( const struct TM* date, char *result ) {
/* I figure everything else can be displayed, even hour 25, but if
these are out of range we walk off the name arrays */
if( !valid_tm_wday(date) || !valid_tm_mon(date) )
return NULL;
sprintf(result, TM64_ASCTIME_FORMAT,
wday_name[date->tm_wday],
mon_name[date->tm_mon],
date->tm_mday, date->tm_hour,
date->tm_min, date->tm_sec,
1900 + date->tm_year);
return result;
}
char *ctime64_r( const Time64_T* time, char* result ) {
struct TM date;
localtime64_r( time, &date );
return asctime64_r( &date, result );
}

81
lib/plist/time64.h Executable file
View File

@@ -0,0 +1,81 @@
#ifndef TIME64_H
# define TIME64_H
#include <time.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Set our custom types */
typedef long long Int64;
typedef Int64 Time64_T;
typedef Int64 Year;
/* A copy of the tm struct but with a 64 bit year */
struct TM64 {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
Year tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
#ifdef HAVE_TM_TM_GMTOFF
long tm_gmtoff;
#endif
#ifdef HAVE_TM_TM_ZONE
char *tm_zone;
#endif
};
/* Decide which tm struct to use */
#ifdef USE_TM64
#define TM TM64
#else
#define TM tm
#endif
/* Declare public functions */
struct TM *gmtime64_r (const Time64_T *, struct TM *);
struct TM *localtime64_r (const Time64_T *, struct TM *);
char *asctime64_r (const struct TM *, char *);
char *ctime64_r (const Time64_T*, char*);
Time64_T timegm64 (const struct TM *);
Time64_T mktime64 (struct TM *);
Time64_T timelocal64 (struct TM *);
/* Not everyone has gm/localtime_r(), provide a replacement */
#ifdef HAVE_LOCALTIME_R
# define LOCALTIME_R(clock, result) localtime_r(clock, result)
#else
# define LOCALTIME_R(clock, result) fake_localtime_r(clock, result)
#endif
#ifdef HAVE_GMTIME_R
# define GMTIME_R(clock, result) gmtime_r(clock, result)
#else
# define GMTIME_R(clock, result) fake_gmtime_r(clock, result)
#endif
/* Use a different asctime format depending on how big the year is */
#ifdef USE_TM64
#define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %lld\n"
#else
#define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
#endif
void copy_tm_to_TM64(const struct tm *src, struct TM *dest);
void copy_TM64_to_tm(const struct TM *src, struct tm *dest);
#endif

97
lib/plist/time64_limits.h Executable file
View File

@@ -0,0 +1,97 @@
/*
Maximum and minimum inputs your system's respective time functions
can correctly handle. time64.h will use your system functions if
the input falls inside these ranges and corresponding USE_SYSTEM_*
constant is defined.
*/
#ifndef TIME64_LIMITS_H
#define TIME64_LIMITS_H
#include <time.h>
/* Max/min for localtime() */
#define SYSTEM_LOCALTIME_MAX 2147483647
#define SYSTEM_LOCALTIME_MIN -2147483647-1
/* Max/min for gmtime() */
#define SYSTEM_GMTIME_MAX 2147483647
#define SYSTEM_GMTIME_MIN -2147483647-1
/* Max/min for mktime() */
static const struct tm SYSTEM_MKTIME_MAX = {
7,
14,
19,
18,
0,
138,
1,
17,
0
#ifdef HAVE_TM_TM_GMTOFF
,-28800
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"PST"
#endif
};
static const struct tm SYSTEM_MKTIME_MIN = {
52,
45,
12,
13,
11,
1,
5,
346,
0
#ifdef HAVE_TM_TM_GMTOFF
,-28800
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"PST"
#endif
};
/* Max/min for timegm() */
#ifdef HAVE_TIMEGM
static const struct tm SYSTEM_TIMEGM_MAX = {
7,
14,
3,
19,
0,
138,
2,
18,
0
#ifdef HAVE_TM_TM_GMTOFF
,0
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"UTC"
#endif
};
static const struct tm SYSTEM_TIMEGM_MIN = {
52,
45,
20,
13,
11,
1,
5,
346,
0
#ifdef HAVE_TM_TM_GMTOFF
,0
#endif
#ifdef HAVE_TM_TM_ZONE
,(char*)"UTC"
#endif
};
#endif /* HAVE_TIMEGM */
#endif /* TIME64_LIMITS_H */

1438
lib/plist/xplist.c Executable file

File diff suppressed because it is too large Load Diff

380
lib/raop.c Executable file
View File

@@ -0,0 +1,380 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "raop.h"
#include "raop_rtp.h"
#include "raop_rtp.h"
#include "pairing.h"
#include "httpd.h"
#include "global.h"
#include "fairplay.h"
#include "netutils.h"
#include "logger.h"
#include "compat.h"
#include "raop_rtp_mirror.h"
#include "raop_ntp.h"
struct raop_s {
/* Callbacks for audio and video */
raop_callbacks_t callbacks;
/* Logger instance */
logger_t *logger;
/* Pairing, HTTP daemon and RSA key */
pairing_t *pairing;
httpd_t *httpd;
dnssd_t *dnssd;
unsigned short port;
};
struct raop_conn_s {
raop_t *raop;
raop_ntp_t *raop_ntp;
raop_rtp_t *raop_rtp;
raop_rtp_mirror_t *raop_rtp_mirror;
fairplay_t *fairplay;
pairing_session_t *pairing;
unsigned char *local;
int locallen;
unsigned char *remote;
int remotelen;
};
typedef struct raop_conn_s raop_conn_t;
#include "raop_handlers.h"
static void *
conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen) {
raop_t *raop = opaque;
raop_conn_t *conn;
assert(raop);
conn = calloc(1, sizeof(raop_conn_t));
if (!conn) {
return NULL;
}
conn->raop = raop;
conn->raop_rtp = NULL;
conn->raop_ntp = NULL;
conn->fairplay = fairplay_init(raop->logger);
if (!conn->fairplay) {
free(conn);
return NULL;
}
conn->pairing = pairing_session_init(raop->pairing);
if (!conn->pairing) {
fairplay_destroy(conn->fairplay);
free(conn);
return NULL;
}
if (locallen == 4) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Local: %d.%d.%d.%d",
local[0], local[1], local[2], local[3]);
} else if (locallen == 16) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Local: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
local[0], local[1], local[2], local[3], local[4], local[5], local[6], local[7],
local[8], local[9], local[10], local[11], local[12], local[13], local[14], local[15]);
}
if (remotelen == 4) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Remote: %d.%d.%d.%d",
remote[0], remote[1], remote[2], remote[3]);
} else if (remotelen == 16) {
logger_log(conn->raop->logger, LOGGER_INFO,
"Remote: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
remote[0], remote[1], remote[2], remote[3], remote[4], remote[5], remote[6], remote[7],
remote[8], remote[9], remote[10], remote[11], remote[12], remote[13], remote[14], remote[15]);
}
conn->local = malloc(locallen);
assert(conn->local);
memcpy(conn->local, local, locallen);
conn->remote = malloc(remotelen);
assert(conn->remote);
memcpy(conn->remote, remote, remotelen);
conn->locallen = locallen;
conn->remotelen = remotelen;
if (raop->callbacks.conn_init) {
raop->callbacks.conn_init(raop->callbacks.cls);
}
return conn;
}
static void
conn_request(void *ptr, http_request_t *request, http_response_t **response) {
raop_conn_t *conn = ptr;
logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
const char *method;
const char *url;
const char *cseq;
char *response_data = NULL;
int response_datalen = 0;
method = http_request_get_method(request);
url = http_request_get_url(request);
cseq = http_request_get_header(request, "CSeq");
if (!method || !cseq) {
return;
}
*response = http_response_init("RTSP/1.0", 200, "OK");
http_response_add_header(*response, "CSeq", cseq);
//http_response_add_header(*response, "Apple-Jack-Status", "connected; type=analog");
http_response_add_header(*response, "Server", "AirTunes/220.68");
logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
raop_handler_t handler = NULL;
if (!strcmp(method, "GET") && !strcmp(url, "/info")) {
handler = &raop_handler_info;
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-setup")) {
handler = &raop_handler_pairsetup;
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-verify")) {
handler = &raop_handler_pairverify;
} else if (!strcmp(method, "POST") && !strcmp(url, "/fp-setup")) {
handler = &raop_handler_fpsetup;
} else if (!strcmp(method, "OPTIONS")) {
handler = &raop_handler_options;
} else if (!strcmp(method, "SETUP")) {
handler = &raop_handler_setup;
} else if (!strcmp(method, "GET_PARAMETER")) {
handler = &raop_handler_get_parameter;
} else if (!strcmp(method, "SET_PARAMETER")) {
handler = &raop_handler_set_parameter;
} else if (!strcmp(method, "POST") && !strcmp(url, "/feedback")) {
handler = &raop_handler_feedback;
} else if (!strcmp(method, "RECORD")) {
handler = &raop_handler_record;
} else if (!strcmp(method, "FLUSH")) {
const char *rtpinfo;
int next_seq = -1;
rtpinfo = http_request_get_header(request, "RTP-Info");
if (rtpinfo) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo);
if (!strncmp(rtpinfo, "seq=", 4)) {
next_seq = strtol(rtpinfo + 4, NULL, 10);
}
}
if (conn->raop_rtp) {
raop_rtp_flush(conn->raop_rtp, next_seq);
} else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH");
}
} else if (!strcmp(method, "TEARDOWN")) {
//http_response_add_header(*response, "Connection", "close");
if (conn->raop_rtp != NULL && raop_rtp_is_running(conn->raop_rtp)) {
/* Destroy our RTP session */
raop_rtp_stop(conn->raop_rtp);
} else if (conn->raop_rtp_mirror) {
/* Destroy our sessions */
raop_rtp_destroy(conn->raop_rtp);
conn->raop_rtp = NULL;
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
conn->raop_rtp_mirror = NULL;
}
}
if (handler != NULL) {
handler(conn, request, *response, &response_data, &response_datalen);
}
http_response_finish(*response, response_data, response_datalen);
if (response_data) {
free(response_data);
response_data = NULL;
response_datalen = 0;
}
}
static void
conn_destroy(void *ptr) {
raop_conn_t *conn = ptr;
logger_log(conn->raop->logger, LOGGER_INFO, "Destroying connection");
if (conn->raop->callbacks.conn_destroy) {
conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls);
}
if (conn->raop_ntp) {
raop_ntp_destroy(conn->raop_ntp);
}
if (conn->raop_rtp) {
/* This is done in case TEARDOWN was not called */
raop_rtp_destroy(conn->raop_rtp);
}
if (conn->raop_rtp_mirror) {
/* This is done in case TEARDOWN was not called */
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
}
conn->raop->callbacks.video_flush(conn->raop->callbacks.cls);
free(conn->local);
free(conn->remote);
pairing_session_destroy(conn->pairing);
fairplay_destroy(conn->fairplay);
free(conn);
}
raop_t *
raop_init(int max_clients, raop_callbacks_t *callbacks) {
raop_t *raop;
pairing_t *pairing;
httpd_t *httpd;
httpd_callbacks_t httpd_cbs;
assert(callbacks);
assert(max_clients > 0);
assert(max_clients < 100);
/* Initialize the network */
if (netutils_init() < 0) {
return NULL;
}
/* Validate the callbacks structure */
if (!callbacks->audio_process ||
!callbacks->video_process) {
return NULL;
}
/* Allocate the raop_t structure */
raop = calloc(1, sizeof(raop_t));
if (!raop) {
return NULL;
}
/* Initialize the logger */
raop->logger = logger_init();
pairing = pairing_init_generate();
if (!pairing) {
free(raop);
return NULL;
}
/* Set HTTP callbacks to our handlers */
memset(&httpd_cbs, 0, sizeof(httpd_cbs));
httpd_cbs.opaque = raop;
httpd_cbs.conn_init = &conn_init;
httpd_cbs.conn_request = &conn_request;
httpd_cbs.conn_destroy = &conn_destroy;
/* Initialize the http daemon */
httpd = httpd_init(raop->logger, &httpd_cbs, max_clients);
if (!httpd) {
pairing_destroy(pairing);
free(raop);
return NULL;
}
/* Copy callbacks structure */
memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t));
raop->pairing = pairing;
raop->httpd = httpd;
return raop;
}
void
raop_destroy(raop_t *raop) {
if (raop) {
raop_stop(raop);
pairing_destroy(raop->pairing);
httpd_destroy(raop->httpd);
logger_destroy(raop->logger);
free(raop);
/* Cleanup the network */
netutils_cleanup();
}
}
int
raop_is_running(raop_t *raop) {
assert(raop);
return httpd_is_running(raop->httpd);
}
void
raop_set_log_level(raop_t *raop, int level) {
assert(raop);
logger_set_level(raop->logger, level);
}
void
raop_set_port(raop_t *raop, unsigned short port) {
assert(raop);
raop->port = port;
}
unsigned short
raop_get_port(raop_t *raop) {
assert(raop);
return raop->port;
}
void *
raop_get_callback_cls(raop_t *raop) {
assert(raop);
return raop->callbacks.cls;
}
void
raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls) {
assert(raop);
logger_set_callback(raop->logger, callback, cls);
}
void
raop_set_dnssd(raop_t *raop, dnssd_t *dnssd) {
assert(dnssd);
raop->dnssd = dnssd;
}
int
raop_start(raop_t *raop, unsigned short *port) {
assert(raop);
assert(port);
return httpd_start(raop->httpd, port);
}
void
raop_stop(raop_t *raop) {
assert(raop);
httpd_stop(raop->httpd);
}

69
lib/raop.h Executable file
View File

@@ -0,0 +1,69 @@
#ifndef RAOP_H
#define RAOP_H
#include "dnssd.h"
#include "stream.h"
#include "raop_ntp.h"
#if defined (WIN32) && defined(DLL_EXPORT)
# define RAOP_API __declspec(dllexport)
#else
# define RAOP_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Define syslog style log levels */
#define RAOP_LOG_EMERG 0 /* system is unusable */
#define RAOP_LOG_ALERT 1 /* action must be taken immediately */
#define RAOP_LOG_CRIT 2 /* critical conditions */
#define RAOP_LOG_ERR 3 /* error conditions */
#define RAOP_LOG_WARNING 4 /* warning conditions */
#define RAOP_LOG_NOTICE 5 /* normal but significant condition */
#define RAOP_LOG_INFO 6 /* informational */
#define RAOP_LOG_DEBUG 7 /* debug-level messages */
typedef struct raop_s raop_t;
typedef void (*raop_log_callback_t)(void *cls, int level, const char *msg);
struct raop_callbacks_s {
void* cls;
void (*audio_process)(void *cls, raop_ntp_t *ntp, aac_decode_struct *data);
void (*video_process)(void *cls, raop_ntp_t *ntp, h264_decode_struct *data);
/* Optional but recommended callback functions */
void (*conn_init)(void *cls);
void (*conn_destroy)(void *cls);
void (*audio_flush)(void *cls);
void (*video_flush)(void *cls);
void (*audio_set_volume)(void *cls, float volume);
void (*audio_set_metadata)(void *cls, const void *buffer, int buflen);
void (*audio_set_coverart)(void *cls, const void *buffer, int buflen);
void (*audio_remote_control_id)(void *cls, const char *dacp_id, const char *active_remote_header);
void (*audio_set_progress)(void *cls, unsigned int start, unsigned int curr, unsigned int end);
};
typedef struct raop_callbacks_s raop_callbacks_t;
RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks);
RAOP_API void raop_set_log_level(raop_t *raop, int level);
RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls);
RAOP_API void raop_set_port(raop_t *raop, unsigned short port);
RAOP_API unsigned short raop_get_port(raop_t *raop);
RAOP_API void *raop_get_callback_cls(raop_t *raop);
RAOP_API int raop_start(raop_t *raop, unsigned short *port);
RAOP_API int raop_is_running(raop_t *raop);
RAOP_API void raop_stop(raop_t *raop);
RAOP_API void raop_set_dnssd(raop_t *raop, dnssd_t *dnssd);
RAOP_API void raop_destroy(raop_t *raop);
#ifdef __cplusplus
}
#endif
#endif

332
lib/raop_buffer.c Executable file
View File

@@ -0,0 +1,332 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include "raop_buffer.h"
#include "raop_rtp.h"
#include "crypto.h"
#include "compat.h"
#include "stream.h"
#define RAOP_BUFFER_LENGTH 32
typedef struct {
/* Data available */
int filled;
/* RTP header */
unsigned short seqnum;
uint64_t timestamp;
/* Payload data */
unsigned int payload_size;
void *payload_data;
} raop_buffer_entry_t;
struct raop_buffer_s {
logger_t *logger;
/* Key and IV used for decryption */
unsigned char aeskey[RAOP_AESKEY_LEN];
unsigned char aesiv[RAOP_AESIV_LEN];
/* First and last seqnum */
int is_empty;
unsigned short first_seqnum;
unsigned short last_seqnum;
/* RTP buffer entries */
raop_buffer_entry_t entries[RAOP_BUFFER_LENGTH];
};
void
raop_buffer_init_key_iv(raop_buffer_t *raop_buffer,
const unsigned char *aeskey,
const unsigned char *aesiv,
const unsigned char *ecdh_secret)
{
// Initialization key
unsigned char eaeskey[64];
memcpy(eaeskey, aeskey, 16);
sha_ctx_t *ctx = sha_init();
sha_update(ctx, eaeskey, 16);
sha_update(ctx, ecdh_secret, 32);
sha_final(ctx, eaeskey, NULL);
sha_destroy(ctx);
memcpy(raop_buffer->aeskey, eaeskey, 16);
memcpy(raop_buffer->aesiv, aesiv, RAOP_AESIV_LEN);
#ifdef DUMP_AUDIO
if (file_keyiv != NULL) {
fwrite(raop_buffer->aeskey, 16, 1, file_keyiv);
fwrite(raop_buffer->aesiv, 16, 1, file_keyiv);
fclose(file_keyiv);
}
#endif
}
raop_buffer_t *
raop_buffer_init(logger_t *logger,
const unsigned char *aeskey,
const unsigned char *aesiv,
const unsigned char *ecdh_secret)
{
raop_buffer_t *raop_buffer;
assert(aeskey);
assert(aesiv);
assert(ecdh_secret);
raop_buffer = calloc(1, sizeof(raop_buffer_t));
if (!raop_buffer) {
return NULL;
}
raop_buffer->logger = logger;
raop_buffer_init_key_iv(raop_buffer, aeskey, aesiv, ecdh_secret);
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
raop_buffer_entry_t *entry = &raop_buffer->entries[i];
entry->payload_data = NULL;
entry->payload_size = 0;
}
raop_buffer->is_empty = 1;
return raop_buffer;
}
void
raop_buffer_destroy(raop_buffer_t *raop_buffer)
{
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
raop_buffer_entry_t *entry = &raop_buffer->entries[i];
if (entry->payload_data != NULL) {
free(entry->payload_data);
}
}
if (raop_buffer) {
free(raop_buffer);
}
#ifdef DUMP_AUDIO
if (file_aac != NULL) {
fclose(file_aac);
}
if (file_source != NULL) {
fclose(file_source);
}
#endif
}
static short
seqnum_cmp(unsigned short s1, unsigned short s2)
{
return (s1 - s2);
}
//#define DUMP_AUDIO
#ifdef DUMP_AUDIO
static FILE* file_aac = NULL;
static FILE* file_source = NULL;
static FILE* file_keyiv = NULL;
#endif
int
raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output, unsigned int payload_size, unsigned int *outputlen)
{
assert(raop_buffer);
int encryptedlen;
#ifdef DUMP_AUDIO
if (file_aac == NULL) {
file_aac = fopen("/home/pi/Airplay.aac", "wb");
file_source = fopen("/home/pi/Airplay.source", "wb");
file_keyiv = fopen("/home/pi/Airplay.keyiv", "wb");
}
// Undecrypted file
if (file_source != NULL) {
fwrite(&data[12], payloadsize, 1, file_source);
}
#endif
encryptedlen = payload_size / 16*16;
memset(output, 0, payload_size);
// Need to be initialized internally
aes_ctx_t *aes_ctx_audio = aes_cbc_init(raop_buffer->aeskey, raop_buffer->aesiv, AES_DECRYPT);
aes_cbc_decrypt(aes_ctx_audio, &data[12], output, encryptedlen);
aes_cbc_destroy(aes_ctx_audio);
memcpy(output + encryptedlen, &data[12 + encryptedlen], payload_size - encryptedlen);
*outputlen = payload_size;
#ifdef DUMP_AUDIO
// Decrypted file
if (file_aac != NULL) {
fwrite(output, payloadsize, 1, file_aac);
}
#endif
return 1;
}
int
raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum) {
assert(raop_buffer);
/* Check packet data length is valid */
if (datalen < 12 || datalen > RAOP_PACKET_LEN) {
return -1;
}
if (datalen == 16 && data[12] == 0x0 && data[13] == 0x68 && data[14] == 0x34 && data[15] == 0x0) {
return 0;
}
int payload_size = datalen - 12;
/* Get correct seqnum for the packet */
unsigned short seqnum;
if (use_seqnum) {
seqnum = (data[2] << 8) | data[3];
} else {
seqnum = raop_buffer->first_seqnum;
}
/* If this packet is too late, just skip it */
if (!raop_buffer->is_empty && seqnum_cmp(seqnum, raop_buffer->first_seqnum) < 0) {
return 0;
}
/* Check that there is always space in the buffer, otherwise flush */
if (seqnum_cmp(seqnum, raop_buffer->first_seqnum + RAOP_BUFFER_LENGTH) >= 0) {
raop_buffer_flush(raop_buffer, seqnum);
}
/* Get entry corresponding our seqnum */
raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH];
if (entry->filled && seqnum_cmp(entry->seqnum, seqnum) == 0) {
/* Packet resend, we can safely ignore */
return 0;
}
/* Update the raop_buffer entry header */
entry->seqnum = seqnum;
entry->timestamp = timestamp;
entry->filled = 1;
entry->payload_data = malloc(payload_size);
int decrypt_ret = raop_buffer_decrypt(raop_buffer, data, entry->payload_data, payload_size, &entry->payload_size);
assert(decrypt_ret >= 0);
assert(entry->payload_size <= payload_size);
/* Update the raop_buffer seqnums */
if (raop_buffer->is_empty) {
raop_buffer->first_seqnum = seqnum;
raop_buffer->last_seqnum = seqnum;
raop_buffer->is_empty = 0;
}
if (seqnum_cmp(seqnum, raop_buffer->last_seqnum) > 0) {
raop_buffer->last_seqnum = seqnum;
}
return 1;
}
void *
raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, int no_resend) {
assert(raop_buffer);
/* Calculate number of entries in the current buffer */
short entry_count = seqnum_cmp(raop_buffer->last_seqnum, raop_buffer->first_seqnum)+1;
/* Cannot dequeue from empty buffer */
if (raop_buffer->is_empty || entry_count <= 0) {
return NULL;
}
/* Get the first buffer entry for inspection */
raop_buffer_entry_t *entry = &raop_buffer->entries[raop_buffer->first_seqnum % RAOP_BUFFER_LENGTH];
if (no_resend) {
/* If we do no resends, always return the first entry */
} else if (!entry->filled) {
/* Check how much we have space left in the buffer */
if (entry_count < RAOP_BUFFER_LENGTH) {
/* Return nothing and hope resend gets on time */
return NULL;
}
/* Risk of buffer overrun, return empty buffer */
}
/* Update buffer and validate entry */
raop_buffer->first_seqnum += 1;
if (!entry->filled) {
return NULL;
}
entry->filled = 0;
/* Return entry payload buffer */
*timestamp = entry->timestamp;
*length = entry->payload_size;
entry->payload_size = 0;
void* data = entry->payload_data;
entry->payload_data = NULL;
return data;
}
void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque) {
assert(raop_buffer);
assert(resend_cb);
if (seqnum_cmp(raop_buffer->first_seqnum, raop_buffer->last_seqnum) < 0) {
int seqnum, count;
for (seqnum = raop_buffer->first_seqnum; seqnum_cmp(seqnum, raop_buffer->last_seqnum) < 0; seqnum++) {
raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH];
if (entry->filled) {
break;
}
}
if (seqnum_cmp(seqnum, raop_buffer->first_seqnum) == 0) {
return;
}
count = seqnum_cmp(seqnum, raop_buffer->first_seqnum);
resend_cb(opaque, raop_buffer->first_seqnum, count);
}
}
void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) {
assert(raop_buffer);
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
if (raop_buffer->entries[i].payload_data) {
free(raop_buffer->entries[i].payload_data);
raop_buffer->entries[i].payload_size = 0;
}
raop_buffer->entries[i].filled = 0;
}
if (next_seq < 0 || next_seq > 0xffff) {
raop_buffer->is_empty = 1;
} else {
raop_buffer->first_seqnum = next_seq;
raop_buffer->last_seqnum = next_seq - 1;
}
}

38
lib/raop_buffer.h Executable file
View File

@@ -0,0 +1,38 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef RAOP_BUFFER_H
#define RAOP_BUFFER_H
#include "logger.h"
#include "raop_rtp.h"
typedef struct raop_buffer_s raop_buffer_t;
typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned short count);
raop_buffer_t *raop_buffer_init(logger_t *logger,
const unsigned char *aeskey,
const unsigned char *aesiv,
const unsigned char *ecdh_secret);
int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum);
void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, int no_resend);
void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque);
void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq);
int raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output,
unsigned int datalen, unsigned int *outputlen);
void raop_buffer_destroy(raop_buffer_t *raop_buffer);
#endif

574
lib/raop_handlers.h Executable file
View File

@@ -0,0 +1,574 @@
/**
* Copyright (C) 2018 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
/* This file should be only included from raop.c as it defines static handler
* functions and depends on raop internals */
#include "plist/plist/plist.h"
#include "dnssdint.h"
#include "utils.h"
#include <ctype.h>
#include <stdlib.h>
typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *,
http_response_t *, char **, int *);
static void
raop_handler_info(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
assert(conn->raop->dnssd);
int airplay_txt_len = 0;
const char *airplay_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &airplay_txt_len);
int name_len = 0;
const char *name = dnssd_get_name(conn->raop->dnssd, &name_len);
int hw_addr_raw_len = 0;
const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len);
char *hw_addr = calloc(1, 3 * hw_addr_raw_len);
int hw_addr_len = utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len);
int pk_len = 0;
char *pk = utils_parse_hex(AIRPLAY_PK, strlen(AIRPLAY_PK), &pk_len);
plist_t r_node = plist_new_dict();
plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len);
plist_dict_set_item(r_node, "txtAirPlay", txt_airplay_node);
plist_t features_node = plist_new_uint((uint64_t) 0x1E << 32 | 0x5A7FFFF7);
plist_dict_set_item(r_node, "features", features_node);
plist_t name_node = plist_new_string(name);
plist_dict_set_item(r_node, "name", name_node);
plist_t audio_formats_node = plist_new_array();
plist_t audio_format_0_node = plist_new_dict();
plist_t audio_format_0_type_node = plist_new_uint(100);
plist_t audio_format_0_audio_input_formats_node = plist_new_uint(67108860);
plist_t audio_format_0_audio_output_formats_node = plist_new_uint(67108860);
plist_dict_set_item(audio_format_0_node, "type", audio_format_0_type_node);
plist_dict_set_item(audio_format_0_node, "audioInputFormats", audio_format_0_audio_input_formats_node);
plist_dict_set_item(audio_format_0_node, "audioOutputFormats", audio_format_0_audio_output_formats_node);
plist_array_append_item(audio_formats_node, audio_format_0_node);
plist_t audio_format_1_node = plist_new_dict();
plist_t audio_format_1_type_node = plist_new_uint(101);
plist_t audio_format_1_audio_input_formats_node = plist_new_uint(67108860);
plist_t audio_format_1_audio_output_formats_node = plist_new_uint(67108860);
plist_dict_set_item(audio_format_1_node, "type", audio_format_1_type_node);
plist_dict_set_item(audio_format_1_node, "audioInputFormats", audio_format_1_audio_input_formats_node);
plist_dict_set_item(audio_format_1_node, "audioOutputFormats", audio_format_1_audio_output_formats_node);
plist_array_append_item(audio_formats_node, audio_format_1_node);
plist_dict_set_item(r_node, "audioFormats", audio_formats_node);
plist_t pi_node = plist_new_string(AIRPLAY_PI);
plist_dict_set_item(r_node, "pi", pi_node);
plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10));
plist_dict_set_item(r_node, "vv", vv_node);
plist_t status_flags_node = plist_new_uint(68);
plist_dict_set_item(r_node, "statusFlags", status_flags_node);
plist_t keep_alive_low_power_node = plist_new_bool(1);
plist_dict_set_item(r_node, "keepAliveLowPower", keep_alive_low_power_node);
plist_t source_version_node = plist_new_string(AIRPLAY_SRCVERS);
plist_dict_set_item(r_node, "sourceVersion", source_version_node);
plist_t pk_node = plist_new_data(pk, pk_len);
plist_dict_set_item(r_node, "pk", pk_node);
plist_t keep_alive_send_stats_as_body_node = plist_new_bool(1);
plist_dict_set_item(r_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node);
plist_t device_id_node = plist_new_string(hw_addr);
plist_dict_set_item(r_node, "deviceID", device_id_node);
plist_t audio_latencies_node = plist_new_array();
plist_t audio_latencies_0_node = plist_new_dict();
plist_t audio_latencies_0_output_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_0_type_node = plist_new_uint(100);
plist_t audio_latencies_0_audio_type_node = plist_new_string("default");
plist_t audio_latencies_0_input_latency_micros_node = plist_new_uint(0);
plist_dict_set_item(audio_latencies_0_node, "outputLatencyMicros", audio_latencies_0_output_latency_micros_node);
plist_dict_set_item(audio_latencies_0_node, "type", audio_latencies_0_type_node);
plist_dict_set_item(audio_latencies_0_node, "audioType", audio_latencies_0_audio_type_node);
plist_dict_set_item(audio_latencies_0_node, "inputLatencyMicros", audio_latencies_0_input_latency_micros_node);
plist_array_append_item(audio_latencies_node, audio_latencies_0_node);
plist_t audio_latencies_1_node = plist_new_dict();
plist_t audio_latencies_1_output_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_1_type_node = plist_new_uint(101);
plist_t audio_latencies_1_audio_type_node = plist_new_string("default");
plist_t audio_latencies_1_input_latency_micros_node = plist_new_uint(0);
plist_dict_set_item(audio_latencies_1_node, "outputLatencyMicros", audio_latencies_1_output_latency_micros_node);
plist_dict_set_item(audio_latencies_1_node, "type", audio_latencies_1_type_node);
plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node);
plist_dict_set_item(audio_latencies_1_node, "inputLatencyMicros", audio_latencies_1_input_latency_micros_node);
plist_array_append_item(audio_latencies_node, audio_latencies_1_node);
plist_dict_set_item(r_node, "audioLatencies", audio_latencies_node);
plist_t model_node = plist_new_string(GLOBAL_MODEL);
plist_dict_set_item(r_node, "model", model_node);
plist_t mac_address_node = plist_new_string(hw_addr);
plist_dict_set_item(r_node, "macAddress", mac_address_node);
plist_t displays_node = plist_new_array();
plist_t displays_0_node = plist_new_dict();
plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925");
plist_t displays_0_width_physical_node = plist_new_uint(0);
plist_t displays_0_height_physical_node = plist_new_uint(0);
plist_t displays_0_width_node = plist_new_uint(1920);
plist_t displays_0_height_node = plist_new_uint(1080);
plist_t displays_0_width_pixels_node = plist_new_uint(1920);
plist_t displays_0_height_pixels_node = plist_new_uint(1080);
plist_t displays_0_rotation_node = plist_new_bool(0);
plist_t displays_0_refresh_rate_node = plist_new_real(1.0 / 60.0);
plist_t displays_0_overscanned_node = plist_new_bool(1);
plist_t displays_0_features = plist_new_uint(14);
plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node);
plist_dict_set_item(displays_0_node, "widthPhysical", displays_0_width_physical_node);
plist_dict_set_item(displays_0_node, "heightPhysical", displays_0_height_physical_node);
plist_dict_set_item(displays_0_node, "width", displays_0_width_node);
plist_dict_set_item(displays_0_node, "height", displays_0_height_node);
plist_dict_set_item(displays_0_node, "widthPixels", displays_0_width_pixels_node);
plist_dict_set_item(displays_0_node, "heightPixels", displays_0_height_pixels_node);
plist_dict_set_item(displays_0_node, "rotation", displays_0_rotation_node);
plist_dict_set_item(displays_0_node, "refreshRate", displays_0_refresh_rate_node);
plist_dict_set_item(displays_0_node, "overscanned", displays_0_overscanned_node);
plist_dict_set_item(displays_0_node, "features", displays_0_features);
plist_array_append_item(displays_node, displays_0_node);
plist_dict_set_item(r_node, "displays", displays_node);
plist_to_bin(r_node, response_data, (uint32_t *) response_datalen);
logger_log(conn->raop->logger, LOGGER_DEBUG, "INFO len = %d", response_datalen);
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
free(pk);
free(hw_addr);
}
static void
raop_handler_pairsetup(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
unsigned char public_key[32];
const char *data;
int datalen;
data = http_request_get_data(request, &datalen);
if (datalen != 32) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data");
return;
}
pairing_get_public_key(conn->raop->pairing, public_key);
pairing_session_set_setup_status(conn->pairing);
*response_data = malloc(sizeof(public_key));
if (*response_data) {
http_response_add_header(response, "Content-Type", "application/octet-stream");
memcpy(*response_data, public_key, sizeof(public_key));
*response_datalen = sizeof(public_key);
}
}
static void
raop_handler_pairverify(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
if (pairing_session_check_handshake_status(conn->pairing)) {
return;
}
unsigned char public_key[32];
unsigned char signature[64];
const unsigned char *data;
int datalen;
data = (unsigned char *) http_request_get_data(request, &datalen);
if (datalen < 4) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
return;
}
switch (data[0]) {
case 1:
if (datalen != 4 + 32 + 32) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
return;
}
/* We can fall through these errors, the result will just be garbage... */
if (pairing_session_handshake(conn->pairing, data + 4, data + 4 + 32)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake");
}
if (pairing_session_get_public_key(conn->pairing, public_key)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key");
}
if (pairing_session_get_signature(conn->pairing, signature)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature");
}
*response_data = malloc(sizeof(public_key) + sizeof(signature));
if (*response_data) {
http_response_add_header(response, "Content-Type", "application/octet-stream");
memcpy(*response_data, public_key, sizeof(public_key));
memcpy(*response_data + sizeof(public_key), signature, sizeof(signature));
*response_datalen = sizeof(public_key) + sizeof(signature);
}
break;
case 0:
if (datalen != 4 + 64) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
return;
}
if (pairing_session_finish(conn->pairing, data + 4)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature");
http_response_set_disconnect(response, 1);
return;
}
http_response_add_header(response, "Content-Type", "application/octet-stream");
break;
}
}
static void
raop_handler_fpsetup(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
const unsigned char *data;
int datalen;
data = (unsigned char *) http_request_get_data(request, &datalen);
if (datalen == 16) {
*response_data = malloc(142);
if (*response_data) {
http_response_add_header(response, "Content-Type", "application/octet-stream");
if (!fairplay_setup(conn->fairplay, data, (unsigned char *) *response_data)) {
*response_datalen = 142;
} else {
// Handle error?
free(*response_data);
*response_data = NULL;
}
}
} else if (datalen == 164) {
*response_data = malloc(32);
if (*response_data) {
http_response_add_header(response, "Content-Type", "application/octet-stream");
if (!fairplay_handshake(conn->fairplay, data, (unsigned char *) *response_data)) {
*response_datalen = 32;
} else {
// Handle error?
free(*response_data);
*response_data = NULL;
}
}
} else {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid fp-setup data length");
return;
}
}
static void
raop_handler_options(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
http_response_add_header(response, "Public", "SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
}
static void
raop_handler_setup(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
unsigned short remote_cport = 0;
const char *transport;
int use_udp;
const char *dacp_id;
const char *active_remote_header;
const char *data;
int data_len;
data = http_request_get_data(request, &data_len);
dacp_id = http_request_get_header(request, "DACP-ID");
active_remote_header = http_request_get_header(request, "Active-Remote");
if (dacp_id && active_remote_header) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id);
logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header);
if (conn->raop_rtp) {
raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header);
}
}
transport = http_request_get_header(request, "Transport");
if (transport) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Transport: %s", transport);
use_udp = strncmp(transport, "RTP/AVP/TCP", 11);
} else {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Transport: null");
use_udp = 0;
}
// Parsing bplist
plist_t req_root_node = NULL;
plist_from_bin(data, data_len, &req_root_node);
plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams");
plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv");
plist_t req_ekey_node = plist_dict_get_item(req_root_node, "ekey");
// For the response
plist_t res_root_node = plist_new_dict();
if (PLIST_IS_DATA(req_eiv_node) && PLIST_IS_DATA(req_ekey_node)) {
// The first SETUP call that initializes keys and timing
unsigned char aesiv[16];
unsigned char aeskey[16];
logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1");
// First setup
char* eiv = NULL;
uint64_t eiv_len = 0;
plist_get_data_val(req_eiv_node, &eiv, &eiv_len);
memcpy(aesiv, eiv, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len);
char* ekey = NULL;
uint64_t ekey_len = 0;
plist_get_data_val(req_ekey_node, &ekey, &ekey_len);
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len);
// ekey is 72 bytes, aeskey is 16 bytes
int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) ekey, aeskey);
logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret);
unsigned char ecdh_secret[32];
pairing_get_ecdh_secret_key(conn->pairing, ecdh_secret);
// Time port
uint64_t timing_rport;
plist_t time_note = plist_dict_get_item(req_root_node, "timingPort");
plist_get_uint_val(time_note, &timing_rport);
logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport);
unsigned short timing_lport;
conn->raop_ntp = raop_ntp_init(conn->raop->logger, conn->remote, conn->remotelen, timing_rport);
raop_ntp_start(conn->raop_ntp, &timing_lport);
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, aesiv, ecdh_secret);
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, ecdh_secret);
plist_t res_event_port_node = plist_new_uint(conn->raop->port);
plist_t res_timing_port_node = plist_new_uint(timing_lport);
plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node);
plist_dict_set_item(res_root_node, "eventPort", res_event_port_node);
logger_log(conn->raop->logger, LOGGER_DEBUG, "eport = %d, tport = %d", conn->raop->port, timing_lport);
}
// Process stream setup requests
if (PLIST_IS_ARRAY(req_streams_node)) {
plist_t res_streams_node = plist_new_array();
int count = plist_array_get_size(req_streams_node);
for (int i = 0; i < count; i++) {
plist_t req_stream_node = plist_array_get_item(req_streams_node, i);
plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
uint64_t type;
plist_get_uint_val(req_stream_type_node, &type);
logger_log(conn->raop->logger, LOGGER_DEBUG, "type = %llu", type);
switch (type) {
case 110: {
// Mirroring
unsigned short dport = 0;
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
uint64_t stream_connection_id;
plist_get_uint_val(stream_id_node, &stream_connection_id);
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID = %llu", stream_connection_id);
if (conn->raop_rtp_mirror) {
raop_rtp_init_mirror_aes(conn->raop_rtp_mirror, stream_connection_id);
raop_rtp_start_mirror(conn->raop_rtp_mirror, use_udp, &dport);
logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
} else {
logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
http_response_set_disconnect(response, 1);
}
plist_t res_stream_node = plist_new_dict();
plist_t res_stream_data_port_node = plist_new_uint(dport);
plist_t res_stream_type_node = plist_new_uint(110);
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
plist_array_append_item(res_streams_node, res_stream_node);
break;
} case 96: {
// Audio
unsigned short cport = 0, dport = 0;
if (conn->raop_rtp) {
raop_rtp_start_audio(conn->raop_rtp, use_udp, remote_cport, &cport, &dport);
logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success");
} else {
logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
http_response_set_disconnect(response, 1);
}
plist_t res_stream_node = plist_new_dict();
plist_t res_stream_data_port_node = plist_new_uint(dport);
plist_t res_stream_control_port_node = plist_new_uint(cport);
plist_t res_stream_type_node = plist_new_uint(96);
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
plist_dict_set_item(res_stream_node, "controlPort", res_stream_control_port_node);
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
plist_array_append_item(res_streams_node, res_stream_node);
break;
}
default:
logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type);
http_response_set_disconnect(response, 1);
break;
}
}
plist_dict_set_item(res_root_node, "streams", res_streams_node);
}
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
}
static void
raop_handler_get_parameter(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
const char *content_type;
const char *data;
int datalen;
content_type = http_request_get_header(request, "Content-Type");
data = http_request_get_data(request, &datalen);
if (!strcmp(content_type, "text/parameters")) {
const char *current = data;
while (current) {
const char *next;
int handled = 0;
/* This is a bit ugly, but seems to be how airport works too */
if (!strncmp(current, "volume\r\n", 8)) {
const char volume[] = "volume: 0.0\r\n";
http_response_add_header(response, "Content-Type", "text/parameters");
*response_data = strdup(volume);
if (*response_data) {
*response_datalen = strlen(*response_data);
}
handled = 1;
}
next = strstr(current, "\r\n");
if (next && !handled) {
logger_log(conn->raop->logger, LOGGER_WARNING,
"Found an unknown parameter: %.*s", (next - current), current);
current = next + 2;
} else if (next) {
current = next + 2;
} else {
current = NULL;
}
}
}
}
static void
raop_handler_set_parameter(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
const char *content_type;
const char *data;
int datalen;
content_type = http_request_get_header(request, "Content-Type");
data = http_request_get_data(request, &datalen);
if (!strcmp(content_type, "text/parameters")) {
char *datastr;
datastr = calloc(1, datalen+1);
if (data && datastr && conn->raop_rtp) {
memcpy(datastr, data, datalen);
if (!strncmp(datastr, "volume: ", 8)) {
float vol = 0.0;
sscanf(datastr+8, "%f", &vol);
raop_rtp_set_volume(conn->raop_rtp, vol);
} else if (!strncmp(datastr, "progress: ", 10)) {
unsigned int start, curr, end;
sscanf(datastr+10, "%u/%u/%u", &start, &curr, &end);
raop_rtp_set_progress(conn->raop_rtp, start, curr, end);
}
} else if (!conn->raop_rtp) {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER");
}
free(datastr);
} else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) {
logger_log(conn->raop->logger, LOGGER_INFO, "Got image data of %d bytes", datalen);
if (conn->raop_rtp) {
raop_rtp_set_coverart(conn->raop_rtp, data, datalen);
} else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart");
}
} else if (!strcmp(content_type, "application/x-dmap-tagged")) {
logger_log(conn->raop->logger, LOGGER_INFO, "Got metadata of %d bytes", datalen);
if (conn->raop_rtp) {
raop_rtp_set_metadata(conn->raop_rtp, data, datalen);
} else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata");
}
}
}
static void
raop_handler_feedback(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_feedback");
}
static void
raop_handler_record(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen)
{
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_record");
http_response_add_header(response, "Audio-Latency", "11025");
http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog");
}

446
lib/raop_ntp.c Normal file
View File

@@ -0,0 +1,446 @@
/*
* Copyright (c) 2019 dsafa22 and 2014 Joakim Plate, modified by Florian Draschbacher,
* All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
// Some of the code in here comes from https://github.com/juhovh/shairplay/pull/25/files
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "raop_ntp.h"
#include "threads.h"
#include "compat.h"
#include "netutils.h"
#include "byteutils.h"
#define RAOP_NTP_DATA_COUNT 8
#define RAOP_NTP_PHI_PPM 15ull // PPM
#define RAOP_NTP_R_RHO ((1ull << 32) / 1000u) // packet precision
#define RAOP_NTP_S_RHO ((1ull << 32) / 1000u) // system clock precision
#define RAOP_NTP_MAX_DIST ((1500ull << 32) / 1000u) // maximum allowed distance
#define RAOP_NTP_MAX_DISP ((16ull << 32)) // maximum dispersion
#define RAOP_NTP_CLOCK_BASE (2208988800ull << 32)
typedef struct raop_ntp_data_s {
uint64_t time; // The local wall clock time at time of ntp packet arrival
uint64_t dispersion;
int64_t delay; // The round trip delay
int64_t offset; // The difference between remote and local wall clock time
} raop_ntp_data_t;
struct raop_ntp_s {
logger_t *logger;
thread_handle_t thread;
mutex_handle_t run_mutex;
mutex_handle_t wait_mutex;
cond_handle_t wait_cond;
raop_ntp_data_t data[RAOP_NTP_DATA_COUNT];
int data_index;
// The clock sync params are periodically updated to the AirPlay client's NTP clock
mutex_handle_t sync_params_mutex;
int64_t sync_offset;
int64_t sync_dispersion;
int64_t sync_delay;
// Socket address of the AirPlay client
struct sockaddr_storage remote_saddr;
socklen_t remote_saddr_len;
// The remote port of the NTP server on the AirPlay client
unsigned short timing_rport;
// The local port of the NTP client on the AirPlay server
unsigned short timing_lport;
/* MUTEX LOCKED VARIABLES START */
/* These variables only edited mutex locked */
int running;
int joined;
// UDP socket
int tsock;
};
/*
* Used for sorting the data array by delay
*/
static int
raop_ntp_compare(const void* av, const void* bv)
{
const raop_ntp_data_t* a = (const raop_ntp_data_t*)av;
const raop_ntp_data_t* b = (const raop_ntp_data_t*)bv;
if (a->delay < b->delay) {
return -1;
} else if(a->delay > b->delay) {
return 1;
} else {
return 0;
}
}
static int
raop_ntp_parse_remote_address(raop_ntp_t *raop_ntp, const unsigned char *remote_addr, int remote_addr_len)
{
char current[25];
int family;
int ret;
assert(raop_ntp);
if (remote_addr_len == 4) {
family = AF_INET;
} else if (remote_addr_len == 16) {
family = AF_INET6;
} else {
return -1;
}
memset(current, 0, sizeof(current));
sprintf(current, "%d.%d.%d.%d", remote_addr[0], remote_addr[1], remote_addr[2], remote_addr[3]);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp parse remote ip = %s", current);
ret = netutils_parse_address(family, current,
&raop_ntp->remote_saddr,
sizeof(raop_ntp->remote_saddr));
if (ret < 0) {
return -1;
}
raop_ntp->remote_saddr_len = ret;
return 0;
}
raop_ntp_t *raop_ntp_init(logger_t *logger, const unsigned char *remote_addr, int remote_addr_len, unsigned short timing_rport) {
raop_ntp_t *raop_ntp;
assert(logger);
raop_ntp = calloc(1, sizeof(raop_ntp_t));
if (!raop_ntp) {
return NULL;
}
raop_ntp->logger = logger;
raop_ntp->timing_rport = timing_rport;
if (raop_ntp_parse_remote_address(raop_ntp, remote_addr, remote_addr_len) < 0) {
free(raop_ntp);
return NULL;
}
// Set port on the remote address struct
((struct sockaddr_in *) &raop_ntp->remote_saddr)->sin_port = htons(timing_rport);
raop_ntp->running = 0;
raop_ntp->joined = 1;
uint64_t time = raop_ntp_get_local_time(raop_ntp);
for (int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
raop_ntp->data[i].offset = 0ll;
raop_ntp->data[i].delay = RAOP_NTP_MAX_DISP;
raop_ntp->data[i].dispersion = RAOP_NTP_MAX_DISP;
raop_ntp->data[i].time = time;
}
raop_ntp->sync_delay = 0;
raop_ntp->sync_dispersion = 0;
raop_ntp->sync_offset = 0;
MUTEX_CREATE(raop_ntp->run_mutex);
MUTEX_CREATE(raop_ntp->wait_mutex);
COND_CREATE(raop_ntp->wait_cond);
MUTEX_CREATE(raop_ntp->sync_params_mutex);
return raop_ntp;
}
void
raop_ntp_destroy(raop_ntp_t *raop_ntp)
{
if (raop_ntp) {
raop_ntp_stop(raop_ntp);
MUTEX_DESTROY(raop_ntp->run_mutex);
MUTEX_DESTROY(raop_ntp->wait_mutex);
COND_DESTROY(raop_ntp->wait_cond);
MUTEX_DESTROY(raop_ntp->sync_params_mutex);
free(raop_ntp);
}
}
unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp) {
return raop_ntp->timing_lport;
}
static int
raop_ntp_init_socket(raop_ntp_t *raop_ntp, int use_ipv6)
{
int tsock = -1;
unsigned short tport = 0;
assert(raop_ntp);
tsock = netutils_init_socket(&tport, use_ipv6, 1);
if (tsock == -1) {
goto sockets_cleanup;
}
// We're calling recvfrom without knowing whether there is any data, so we need a timeout
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 3000;
if (setsockopt(tsock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
goto sockets_cleanup;
}
/* Set socket descriptors */
raop_ntp->tsock = tsock;
/* Set port values */
raop_ntp->timing_lport = tport;
return 0;
sockets_cleanup:
if (tsock != -1) closesocket(tsock);
return -1;
}
static THREAD_RETVAL
raop_ntp_thread(void *arg)
{
raop_ntp_t *raop_ntp = arg;
assert(raop_ntp);
unsigned char response[128];
int response_len;
unsigned char request[32] = {0x80, 0xd2, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
raop_ntp_data_t data_sorted[RAOP_NTP_DATA_COUNT];
const unsigned two_pow_n[RAOP_NTP_DATA_COUNT] = {2, 4, 8, 16, 32, 64, 128, 256};
while (1) {
MUTEX_LOCK(raop_ntp->run_mutex);
if (!raop_ntp->running) {
MUTEX_UNLOCK(raop_ntp->run_mutex);
break;
}
MUTEX_UNLOCK(raop_ntp->run_mutex);
// Send request
uint64_t send_time = raop_ntp_get_local_time(raop_ntp);
byteutils_put_ntp_timestamp(request, 24, send_time);
int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0,
(struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp send_len = %d", send_len);
if (send_len < 0) {
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request");
break;
}
// Read response
response_len = recvfrom(raop_ntp->tsock, (char *)response, sizeof(response), 0,
(struct sockaddr *) &raop_ntp->remote_saddr, &raop_ntp->remote_saddr_len);
if (response_len < 0) {
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp receive timeout");
break;
}
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp receive time type_t packetlen = %d", response_len);
int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp);
// Local time of the client when the NTP request packet leaves the client
int64_t t0 = (int64_t) byteutils_get_ntp_timestamp(response, 8);
// Local time of the server when the NTP request packet arrives at the server
int64_t t1 = (int64_t) byteutils_get_ntp_timestamp(response, 16);
// Local time of the server when the response message leaves the server
int64_t t2 = (int64_t) byteutils_get_ntp_timestamp(response, 24);
// The iOS device sends its time in micro seconds relative to an arbitrary Epoch (the last boot).
// For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970 * 1000000 us.
// This means we have to expect some rather huge offset, but its growth or shrink over time should be small.
raop_ntp->data_index = (raop_ntp->data_index + 1) % RAOP_NTP_DATA_COUNT;
raop_ntp->data[raop_ntp->data_index].time = t3;
raop_ntp->data[raop_ntp->data_index].offset = ((t1 - t0) + (t2 - t3)) / 2;
raop_ntp->data[raop_ntp->data_index].delay = ((t3 - t0) - (t2 - t1));
raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / 1000000u;
// Sort by delay
memcpy(data_sorted, raop_ntp->data, sizeof(data_sorted));
qsort(data_sorted, RAOP_NTP_DATA_COUNT, sizeof(data_sorted[0]), raop_ntp_compare);
uint64_t dispersion = 0ull;
int64_t offset = data_sorted[0].offset;
int64_t delay = data_sorted[RAOP_NTP_DATA_COUNT - 1].delay;
// Calculate dispersion
for(int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / 1000000u;
dispersion += disp / two_pow_n[i];
}
MUTEX_LOCK(raop_ntp->sync_params_mutex);
int64_t correction = offset - raop_ntp->sync_offset;
raop_ntp->sync_offset = offset;
raop_ntp->sync_dispersion = dispersion;
raop_ntp->sync_delay = delay;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp sync correction = %lld", correction);
// Sleep for 3 seconds
struct timeval now;
struct timespec wait_time;
MUTEX_LOCK(raop_ntp->wait_mutex);
gettimeofday(&now, NULL);
wait_time.tv_sec = now.tv_sec + 3;
wait_time.tv_nsec = now.tv_usec * 1000;
pthread_cond_timedwait(&raop_ntp->wait_cond, &raop_ntp->wait_mutex, &wait_time);
MUTEX_UNLOCK(raop_ntp->wait_mutex);
}
// Ensure running reflects the actual state
MUTEX_LOCK(raop_ntp->run_mutex);
raop_ntp->running = false;
MUTEX_UNLOCK(raop_ntp->run_mutex);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp exiting thread");
return 0;
}
void
raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport)
{
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp starting time");
int use_ipv6 = 0;
assert(raop_ntp);
MUTEX_LOCK(raop_ntp->run_mutex);
if (raop_ntp->running || !raop_ntp->joined) {
MUTEX_UNLOCK(raop_ntp->run_mutex);
return;
}
/* Initialize ports and sockets */
if (raop_ntp->remote_saddr.ss_family == AF_INET6) {
use_ipv6 = 1;
}
use_ipv6 = 0;
if (raop_ntp_init_socket(raop_ntp, use_ipv6) < 0) {
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp initializing timing socket failed");
MUTEX_UNLOCK(raop_ntp->run_mutex);
return;
}
if (timing_lport) *timing_lport = raop_ntp->timing_lport;
/* Create the thread and initialize running values */
raop_ntp->running = 1;
raop_ntp->joined = 0;
THREAD_CREATE(raop_ntp->thread, raop_ntp_thread, raop_ntp);
MUTEX_UNLOCK(raop_ntp->run_mutex);
}
void
raop_ntp_stop(raop_ntp_t *raop_ntp)
{
assert(raop_ntp);
/* Check that we are running and thread is not
* joined (should never be while still running) */
MUTEX_LOCK(raop_ntp->run_mutex);
if (!raop_ntp->running || raop_ntp->joined) {
MUTEX_UNLOCK(raop_ntp->run_mutex);
return;
}
raop_ntp->running = 0;
MUTEX_UNLOCK(raop_ntp->run_mutex);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopping time thread");
MUTEX_LOCK(raop_ntp->wait_mutex);
COND_SIGNAL(raop_ntp->wait_cond);
MUTEX_UNLOCK(raop_ntp->wait_mutex);
if (raop_ntp->tsock != -1) {
closesocket(raop_ntp->tsock);
raop_ntp->tsock = -1;
}
THREAD_JOIN(raop_ntp->thread);
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopped time thread");
/* Mark thread as joined */
MUTEX_LOCK(raop_ntp->run_mutex);
raop_ntp->joined = 1;
MUTEX_UNLOCK(raop_ntp->run_mutex);
}
/**
* Converts from a little endian ntp timestamp to micro seconds since the Unix epoch.
* Does the same thing as byteutils_get_ntp_timestamp, except its input is an uint64_t
* and expected to already be in little endian.
* Please note this just converts to a different representation, the clock remains the
* same.
*/
uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) {
uint64_t seconds = ((ntp_timestamp >> 32) & 0xffffffff) - (account_for_epoch_diff ? SECONDS_FROM_1900_TO_1970 : 0);
uint64_t fraction = (ntp_timestamp & 0xffffffff);
return (seconds * 1000000) + ((fraction * 1000000) >> 32);
}
/**
* Returns the current time in micro seconds according to the local wall clock.
* The system Unix time is used as the local wall clock.
*/
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp) {
struct timespec time;
clock_gettime(CLOCK_REALTIME, &time);
return (uint64_t)time.tv_sec * 1000000L + (uint64_t)(time.tv_nsec / 1000);
}
/**
* Returns the current time in micro seconds according to the remote wall clock.
*/
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp) {
MUTEX_LOCK(raop_ntp->sync_params_mutex);
int64_t offset = raop_ntp->sync_offset;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
return (uint64_t) ((int64_t) raop_ntp_get_local_time(raop_ntp)) + ((int64_t) offset);
}
/**
* Returns the local wall clock time in micro seconds for the given point in remote clock time
*/
uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time) {
MUTEX_LOCK(raop_ntp->sync_params_mutex);
uint64_t offset = raop_ntp->sync_offset;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
return (uint64_t) ((int64_t) remote_time) - ((int64_t) offset);
}
/**
* Returns the remote wall clock time in micro seconds for the given point in local clock time
*/
uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time) {
MUTEX_LOCK(raop_ntp->sync_params_mutex);
uint64_t offset = raop_ntp->sync_offset;
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
return (uint64_t) ((int64_t) local_time) + ((int64_t) offset);
}

42
lib/raop_ntp.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2019 dsafa22, modified by Florian Draschbacher,
* All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef RAOP_NTP_H
#define RAOP_NTP_H
#include <stdbool.h>
#include <stdint.h>
#include "logger.h"
typedef struct raop_ntp_s raop_ntp_t;
raop_ntp_t *raop_ntp_init(logger_t *logger, const unsigned char *remote_addr, int remote_addr_len, unsigned short timing_rport);
void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport);
void raop_ntp_stop(raop_ntp_t *raop_ntp);
unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp);
void raop_ntp_destroy(raop_ntp_t *raop_rtp);
uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff);
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp);
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp);
uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time);
uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time);
#endif //RAOP_NTP_H

699
lib/raop_rtp.c Executable file
View File

@@ -0,0 +1,699 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include "raop_rtp.h"
#include "raop.h"
#include "raop_buffer.h"
#include "netutils.h"
#include "compat.h"
#include "logger.h"
#include "byteutils.h"
#include "mirror_buffer.h"
#include "stream.h"
#define NO_FLUSH (-42)
#define RAOP_RTP_SAMPLE_RATE (44100.0 / 1000000.0)
#define RAOP_RTP_SYNC_DATA_COUNT 8
typedef struct raop_rtp_sync_data_s {
uint64_t ntp_time; // The local wall clock time at the time of rtp_time
uint32_t rtp_time; // The remote rtp clock time corresponding to ntp_time
} raop_rtp_sync_data_t;
struct raop_rtp_s {
logger_t *logger;
raop_callbacks_t callbacks;
// Time and sync
raop_ntp_t *ntp;
double rtp_sync_scale;
int64_t rtp_sync_offset;
raop_rtp_sync_data_t sync_data[RAOP_RTP_SYNC_DATA_COUNT];
int sync_data_index;
// Transmission Stats, could be used if a playout buffer is needed
// float interarrival_jitter; // As defined by RTP RFC 3550, Section 6.4.1
// unsigned int last_packet_transit_time;
//int transit = (packet_receive_time - packet_send_time);
// int d = transit - last_packet_transit_time;
// if (d < 0) d = -d;
// interarrival_jitter = (1.f / 16.f) * ((double) d - interarrival_jitter);
/* Buffer to handle all resends */
raop_buffer_t *buffer;
/* Remote address as sockaddr */
struct sockaddr_storage remote_saddr;
socklen_t remote_saddr_len;
/* MUTEX LOCKED VARIABLES START */
/* These variables only edited mutex locked */
int running;
int joined;
float volume;
int volume_changed;
unsigned char *metadata;
int metadata_len;
unsigned char *coverart;
int coverart_len;
char *dacp_id;
char *active_remote_header;
unsigned int progress_start;
unsigned int progress_curr;
unsigned int progress_end;
int progress_changed;
int flush;
thread_handle_t thread;
mutex_handle_t run_mutex;
/* MUTEX LOCKED VARIABLES END */
/* Remote control and timing ports */
unsigned short control_rport;
/* Sockets for control and data */
int csock, dsock;
/* Local control, timing and data ports */
unsigned short control_lport;
unsigned short data_lport;
/* Initialized after the first control packet */
struct sockaddr_storage control_saddr;
socklen_t control_saddr_len;
unsigned short control_seqnum;
};
static int
raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const unsigned char *remote, int remotelen)
{
char current[25];
int family;
int ret;
assert(raop_rtp);
if (remotelen == 4) {
family = AF_INET;
} else if (remotelen == 16) {
family = AF_INET6;
} else {
return -1;
}
memset(current, 0, sizeof(current));
sprintf(current, "%d.%d.%d.%d", remote[0], remote[1], remote[2], remote[3]);
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp parse remote ip = %s", current);
ret = netutils_parse_address(family, current,
&raop_rtp->remote_saddr,
sizeof(raop_rtp->remote_saddr));
if (ret < 0) {
return -1;
}
raop_rtp->remote_saddr_len = ret;
return 0;
}
raop_rtp_t *
raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote, int remotelen,
const unsigned char *aeskey, const unsigned char *aesiv, const unsigned char *ecdh_secret)
{
raop_rtp_t *raop_rtp;
assert(logger);
assert(callbacks);
raop_rtp = calloc(1, sizeof(raop_rtp_t));
if (!raop_rtp) {
return NULL;
}
raop_rtp->logger = logger;
raop_rtp->ntp = ntp;
raop_rtp->rtp_sync_offset = 0;
raop_rtp->rtp_sync_scale = RAOP_RTP_SAMPLE_RATE;
raop_rtp->sync_data_index = 0;
for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; ++i) {
raop_rtp->sync_data[i].ntp_time = 0;
raop_rtp->sync_data[i].rtp_time = 0;
}
memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t));
raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv, ecdh_secret);
if (!raop_rtp->buffer) {
free(raop_rtp);
return NULL;
}
if (raop_rtp_parse_remote(raop_rtp, remote, remotelen) < 0) {
free(raop_rtp);
return NULL;
}
raop_rtp->running = 0;
raop_rtp->joined = 1;
raop_rtp->flush = NO_FLUSH;
MUTEX_CREATE(raop_rtp->run_mutex);
return raop_rtp;
}
void
raop_rtp_destroy(raop_rtp_t *raop_rtp)
{
if (raop_rtp) {
raop_rtp_stop(raop_rtp);
MUTEX_DESTROY(raop_rtp->run_mutex);
raop_buffer_destroy(raop_rtp->buffer);
free(raop_rtp->metadata);
free(raop_rtp->coverart);
free(raop_rtp->dacp_id);
free(raop_rtp->active_remote_header);
free(raop_rtp);
}
}
static int
raop_rtp_resend_callback(void *opaque, unsigned short seqnum, unsigned short count)
{
raop_rtp_t *raop_rtp = opaque;
unsigned char packet[8];
unsigned short ourseqnum;
struct sockaddr *addr;
socklen_t addrlen;
int ret;
addr = (struct sockaddr *)&raop_rtp->control_saddr;
addrlen = raop_rtp->control_saddr_len;
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp got resend request %d %d", seqnum, count);
ourseqnum = raop_rtp->control_seqnum++;
/* Fill the request buffer */
packet[0] = 0x80;
packet[1] = 0x55|0x80;
packet[2] = (ourseqnum >> 8);
packet[3] = ourseqnum;
packet[4] = (seqnum >> 8);
packet[5] = seqnum;
packet[6] = (count >> 8);
packet[7] = count;
ret = sendto(raop_rtp->csock, (const char *)packet, sizeof(packet), 0, addr, addrlen);
if (ret == -1) {
logger_log(raop_rtp->logger, LOGGER_WARNING, "raop_rtp resend failed: %d", SOCKET_GET_ERROR());
}
return 0;
}
static int
raop_rtp_init_sockets(raop_rtp_t *raop_rtp, int use_ipv6, int use_udp)
{
int csock = -1, dsock = -1;
unsigned short cport = 0, dport = 0;
assert(raop_rtp);
csock = netutils_init_socket(&cport, use_ipv6, 1);
dsock = netutils_init_socket(&dport, use_ipv6, 1);
if (csock == -1 || dsock == -1) {
goto sockets_cleanup;
}
/* Set socket descriptors */
raop_rtp->csock = csock;
raop_rtp->dsock = dsock;
/* Set port values */
raop_rtp->control_lport = cport;
raop_rtp->data_lport = dport;
return 0;
sockets_cleanup:
if (csock != -1) closesocket(csock);
if (dsock != -1) closesocket(dsock);
return -1;
}
static int
raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data)
{
int flush;
float volume;
int volume_changed;
unsigned char *metadata;
int metadata_len;
unsigned char *coverart;
int coverart_len;
char *dacp_id;
char *active_remote_header;
unsigned int progress_start;
unsigned int progress_curr;
unsigned int progress_end;
int progress_changed;
assert(raop_rtp);
MUTEX_LOCK(raop_rtp->run_mutex);
if (!raop_rtp->running) {
MUTEX_UNLOCK(raop_rtp->run_mutex);
return 1;
}
/* Read the volume level */
volume = raop_rtp->volume;
volume_changed = raop_rtp->volume_changed;
raop_rtp->volume_changed = 0;
/* Read the flush value */
flush = raop_rtp->flush;
raop_rtp->flush = NO_FLUSH;
/* Read the metadata */
metadata = raop_rtp->metadata;
metadata_len = raop_rtp->metadata_len;
raop_rtp->metadata = NULL;
raop_rtp->metadata_len = 0;
/* Read the coverart */
coverart = raop_rtp->coverart;
coverart_len = raop_rtp->coverart_len;
raop_rtp->coverart = NULL;
raop_rtp->coverart_len = 0;
/* Read DACP remote control data */
dacp_id = raop_rtp->dacp_id;
active_remote_header = raop_rtp->active_remote_header;
raop_rtp->dacp_id = NULL;
raop_rtp->active_remote_header = NULL;
/* Read the progress values */
progress_start = raop_rtp->progress_start;
progress_curr = raop_rtp->progress_curr;
progress_end = raop_rtp->progress_end;
progress_changed = raop_rtp->progress_changed;
raop_rtp->progress_changed = 0;
MUTEX_UNLOCK(raop_rtp->run_mutex);
/* Call set_volume callback if changed */
if (volume_changed) {
raop_buffer_flush(raop_rtp->buffer, flush);
if (raop_rtp->callbacks.audio_set_volume) {
raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, volume);
}
}
/* Handle flush if requested */
if (flush != NO_FLUSH) {
if (raop_rtp->callbacks.audio_flush) {
raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls);
}
}
if (metadata != NULL) {
if (raop_rtp->callbacks.audio_set_metadata) {
raop_rtp->callbacks.audio_set_metadata(raop_rtp->callbacks.cls, metadata, metadata_len);
}
free(metadata);
metadata = NULL;
}
if (coverart != NULL) {
if (raop_rtp->callbacks.audio_set_coverart) {
raop_rtp->callbacks.audio_set_coverart(raop_rtp->callbacks.cls, coverart, coverart_len);
}
free(coverart);
coverart = NULL;
}
if (dacp_id && active_remote_header) {
if (raop_rtp->callbacks.audio_remote_control_id) {
raop_rtp->callbacks.audio_remote_control_id(raop_rtp->callbacks.cls, dacp_id, active_remote_header);
}
free(dacp_id);
free(active_remote_header);
dacp_id = NULL;
active_remote_header = NULL;
}
if (progress_changed) {
if (raop_rtp->callbacks.audio_set_progress) {
raop_rtp->callbacks.audio_set_progress(raop_rtp->callbacks.cls, progress_start, progress_curr, progress_end);
}
}
return 0;
}
void raop_rtp_sync_clock(raop_rtp_t *raop_rtp, uint32_t rtp_time, uint64_t ntp_time) {
raop_rtp->sync_data_index = (raop_rtp->sync_data_index + 1) % RAOP_RTP_SYNC_DATA_COUNT;
raop_rtp->sync_data[raop_rtp->sync_data_index].rtp_time = rtp_time;
raop_rtp->sync_data[raop_rtp->sync_data_index].ntp_time = ntp_time;
uint32_t valid_data_count = 0;
valid_data_count = 0;
int64_t total_offsets = 0;
for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; ++i) {
if (raop_rtp->sync_data[i].ntp_time == 0) continue;
total_offsets += (int64_t) (((double) raop_rtp->sync_data[i].rtp_time) / raop_rtp->rtp_sync_scale) - raop_rtp->sync_data[i].ntp_time;
valid_data_count++;
}
int64_t avg_offset = total_offsets / valid_data_count;
int64_t correction = avg_offset - raop_rtp->rtp_sync_offset;
raop_rtp->rtp_sync_offset = avg_offset;
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp sync correction=%lld", correction);
}
uint64_t raop_rtp_convert_rtp_time(raop_rtp_t *raop_rtp, uint32_t rtp_time) {
return (uint64_t) (((double) rtp_time) / raop_rtp->rtp_sync_scale) - raop_rtp->rtp_sync_offset;
}
static THREAD_RETVAL
raop_rtp_thread_udp(void *arg)
{
raop_rtp_t *raop_rtp = arg;
unsigned char packet[RAOP_PACKET_LEN];
unsigned int packetlen;
struct sockaddr_storage saddr;
socklen_t saddrlen;
assert(raop_rtp);
while(1) {
fd_set rfds;
struct timeval tv;
int nfds, ret;
/* Check if we are still running and process callbacks */
if (raop_rtp_process_events(raop_rtp, NULL)) {
break;
}
/* Set timeout value to 5ms */
tv.tv_sec = 0;
tv.tv_usec = 5000;
/* Get the correct nfds value */
nfds = raop_rtp->csock+1;
if (raop_rtp->dsock >= nfds)
nfds = raop_rtp->dsock+1;
/* Set rfds and call select */
FD_ZERO(&rfds);
FD_SET(raop_rtp->csock, &rfds);
FD_SET(raop_rtp->dsock, &rfds);
ret = select(nfds, &rfds, NULL, NULL, &tv);
if (ret == 0) {
/* Timeout happened */
continue;
} else if (ret == -1) {
logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp error in select");
break;
}
if (FD_ISSET(raop_rtp->csock, &rfds)) {
saddrlen = sizeof(saddr);
packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)&saddr, &saddrlen);
memcpy(&raop_rtp->control_saddr, &saddr, saddrlen);
raop_rtp->control_saddr_len = saddrlen;
int type_c = packet[1] & ~0x80;
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp type_c 0x%02x, packetlen = %d", type_c, packetlen);
if (type_c == 0x56) {
/* Handle resent data packet */
uint32_t rtp_timestamp = (packet[4 + 4] << 24) | (packet[4 + 5] << 16) | (packet[4 + 6] << 8) | packet[4 + 7];
uint64_t ntp_timestamp = raop_rtp_convert_rtp_time(raop_rtp, rtp_timestamp);
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp);
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio resent: ntp = %llu, now = %llu, latency=%lld, rtp=%u",
ntp_timestamp, ntp_now, ((int64_t) ntp_now) - ((int64_t) ntp_timestamp), rtp_timestamp);
int result = raop_buffer_enqueue(raop_rtp->buffer, packet + 4, packetlen - 4, ntp_timestamp, 1);
assert(result >= 0);
} else if (type_c == 0x54 && packetlen >= 20) {
// The unit for the rtp clock is 1 / sample rate = 1 / 44100
uint32_t sync_rtp = byteutils_get_int_be(packet, 4) - 11025;
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
uint64_t sync_ntp_remote = raop_ntp_timestamp_to_micro_seconds(sync_ntp_raw, true);
uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote);
// It's not clear what the additional rtp timestamp indicates
uint32_t next_rtp = byteutils_get_int_be(packet, 16);
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp sync: ntp=%llu, local ntp: %llu, rtp=%u, rtp_next=%u",
sync_ntp_remote, sync_ntp_local, sync_rtp, next_rtp);
raop_rtp_sync_clock(raop_rtp, sync_rtp, sync_ntp_local);
} else {
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp unknown packet");
}
}
if (FD_ISSET(raop_rtp->dsock, &rfds)) {
//logger_log(raop_rtp->logger, LOGGER_INFO, "Would have data packet in queue");
// Receiving audio data here
saddrlen = sizeof(saddr);
packetlen = recvfrom(raop_rtp->dsock, (char *)packet, sizeof(packet), 0,
(struct sockaddr *)&saddr, &saddrlen);
// rtp payload type
int type_d = packet[1] & ~0x80;
//logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp_thread_udp type_d 0x%02x, packetlen = %d", type_d, packetlen);
// Len = 16 appears if there is no time
if (packetlen >= 12) {
int no_resend = (raop_rtp->control_rport == 0);// false
uint32_t rtp_timestamp = (packet[4] << 24) | (packet[5] << 16) | (packet[6] << 8) | packet[7];
uint64_t ntp_timestamp = raop_rtp_convert_rtp_time(raop_rtp, rtp_timestamp);
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp);
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio: ntp = %llu, now = %llu, latency=%lld, rtp=%u",
ntp_timestamp, ntp_now, ((int64_t) ntp_now) - ((int64_t) ntp_timestamp), rtp_timestamp);
int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, ntp_timestamp, 1);
assert(result >= 0);
// Render continuous buffer entries
void *payload = NULL;
unsigned int payload_size;
uint64_t timestamp;
while ((payload = raop_buffer_dequeue(raop_rtp->buffer, &payload_size, &timestamp, no_resend))) {
aac_decode_struct aac_data;
aac_data.data_len = payload_size;
aac_data.data = payload;
aac_data.pts = timestamp;
raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &aac_data);
free(payload);
}
/* Handle possible resend requests */
if (!no_resend) {
raop_buffer_handle_resends(raop_rtp->buffer, raop_rtp_resend_callback, raop_rtp);
}
}
}
}
// Ensure running reflects the actual state
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->running = false;
MUTEX_UNLOCK(raop_rtp->run_mutex);
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp exiting thread");
return 0;
}
// Start rtp service, three udp ports
void
raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport,
unsigned short *control_lport, unsigned short *data_lport)
{
logger_log(raop_rtp->logger, LOGGER_INFO, "raop_rtp starting audio");
int use_ipv6 = 0;
assert(raop_rtp);
MUTEX_LOCK(raop_rtp->run_mutex);
if (raop_rtp->running || !raop_rtp->joined) {
MUTEX_UNLOCK(raop_rtp->run_mutex);
return;
}
/* Initialize ports and sockets */
raop_rtp->control_rport = control_rport;
if (raop_rtp->remote_saddr.ss_family == AF_INET6) {
use_ipv6 = 1;
}
use_ipv6 = 0;
if (raop_rtp_init_sockets(raop_rtp, use_ipv6, use_udp) < 0) {
logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp initializing sockets failed");
MUTEX_UNLOCK(raop_rtp->run_mutex);
return;
}
if (control_lport) *control_lport = raop_rtp->control_lport;
if (data_lport) *data_lport = raop_rtp->data_lport;
/* Create the thread and initialize running values */
raop_rtp->running = 1;
raop_rtp->joined = 0;
THREAD_CREATE(raop_rtp->thread, raop_rtp_thread_udp, raop_rtp);
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume)
{
assert(raop_rtp);
if (volume > 0.0f) {
volume = 0.0f;
} else if (volume < -144.0f) {
volume = -144.0f;
}
/* Set volume in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->volume = volume;
raop_rtp->volume_changed = 1;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen)
{
unsigned char *metadata;
assert(raop_rtp);
if (datalen <= 0) {
return;
}
metadata = malloc(datalen);
assert(metadata);
memcpy(metadata, data, datalen);
/* Set metadata in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->metadata = metadata;
raop_rtp->metadata_len = datalen;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen)
{
unsigned char *coverart;
assert(raop_rtp);
if (datalen <= 0) {
return;
}
coverart = malloc(datalen);
assert(coverart);
memcpy(coverart, data, datalen);
/* Set coverart in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->coverart = coverart;
raop_rtp->coverart_len = datalen;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header)
{
assert(raop_rtp);
if (!dacp_id || !active_remote_header) {
return;
}
/* Set dacp stuff in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->dacp_id = strdup(dacp_id);
raop_rtp->active_remote_header = strdup(active_remote_header);
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end)
{
assert(raop_rtp);
/* Set progress in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->progress_start = start;
raop_rtp->progress_curr = curr;
raop_rtp->progress_end = end;
raop_rtp->progress_changed = 1;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq)
{
assert(raop_rtp);
/* Call flush in thread instead */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->flush = next_seq;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
void
raop_rtp_stop(raop_rtp_t *raop_rtp)
{
assert(raop_rtp);
/* Check that we are running and thread is not
* joined (should never be while still running) */
MUTEX_LOCK(raop_rtp->run_mutex);
if (!raop_rtp->running || raop_rtp->joined) {
MUTEX_UNLOCK(raop_rtp->run_mutex);
return;
}
raop_rtp->running = 0;
MUTEX_UNLOCK(raop_rtp->run_mutex);
/* Join the thread */
THREAD_JOIN(raop_rtp->thread);
if (raop_rtp->csock != -1) closesocket(raop_rtp->csock);
if (raop_rtp->dsock != -1) closesocket(raop_rtp->dsock);
/* Flush buffer into initial state */
raop_buffer_flush(raop_rtp->buffer, -1);
/* Mark thread as joined */
MUTEX_LOCK(raop_rtp->run_mutex);
raop_rtp->joined = 1;
MUTEX_UNLOCK(raop_rtp->run_mutex);
}
int
raop_rtp_is_running(raop_rtp_t *raop_rtp)
{
assert(raop_rtp);
MUTEX_LOCK(raop_rtp->run_mutex);
int running = raop_rtp->running;
MUTEX_UNLOCK(raop_rtp->run_mutex);
return running;
}

45
lib/raop_rtp.h Executable file
View File

@@ -0,0 +1,45 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef RAOP_RTP_H
#define RAOP_RTP_H
/* For raop_callbacks_t */
#include "raop.h"
#include "logger.h"
#include "raop_ntp.h"
#define RAOP_AESIV_LEN 16
#define RAOP_AESKEY_LEN 16
#define RAOP_PACKET_LEN 32768
typedef struct raop_rtp_s raop_rtp_t;
raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote, int remotelen,
const unsigned char *aeskey, const unsigned char *aesiv, const unsigned char *ecdh_secret);
void raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport,
unsigned short *control_lport, unsigned short *data_lport);
void raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume);
void raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen);
void raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen);
void raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header);
void raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end);
void raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq);
void raop_rtp_stop(raop_rtp_t *raop_rtp);
int raop_rtp_is_running(raop_rtp_t *raop_rtp);
void raop_rtp_destroy(raop_rtp_t *raop_rtp);
#endif

513
lib/raop_rtp_mirror.c Executable file
View File

@@ -0,0 +1,513 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include "raop_rtp_mirror.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include "raop.h"
#include "netutils.h"
#include "compat.h"
#include "logger.h"
#include "byteutils.h"
#include "mirror_buffer.h"
#include "stream.h"
struct h264codec_s {
unsigned char compatibility;
short pps_size;
short sps_size;
unsigned char level;
unsigned char number_of_pps;
unsigned char* picture_parameter_set;
unsigned char profile_high;
unsigned char reserved_3_and_sps;
unsigned char reserved_6_and_nal;
unsigned char* sequence_parameter_set;
unsigned char version;
};
struct raop_rtp_mirror_s {
logger_t *logger;
raop_callbacks_t callbacks;
raop_ntp_t *ntp;
/* Buffer to handle all resends */
mirror_buffer_t *buffer;
/* Remote address as sockaddr */
struct sockaddr_storage remote_saddr;
socklen_t remote_saddr_len;
/* MUTEX LOCKED VARIABLES START */
/* These variables only edited mutex locked */
int running;
int joined;
int flush;
thread_handle_t thread_mirror;
mutex_handle_t run_mutex;
/* MUTEX LOCKED VARIABLES END */
int mirror_data_sock;
unsigned short mirror_data_lport;
};
static int
raop_rtp_parse_remote(raop_rtp_mirror_t *raop_rtp_mirror, const unsigned char *remote, int remotelen)
{
char current[25];
int family;
int ret;
assert(raop_rtp_mirror);
if (remotelen == 4) {
family = AF_INET;
} else if (remotelen == 16) {
family = AF_INET6;
} else {
return -1;
}
memset(current, 0, sizeof(current));
sprintf(current, "%d.%d.%d.%d", remote[0], remote[1], remote[2], remote[3]);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror parse remote ip = %s", current);
ret = netutils_parse_address(family, current,
&raop_rtp_mirror->remote_saddr,
sizeof(raop_rtp_mirror->remote_saddr));
if (ret < 0) {
return -1;
}
raop_rtp_mirror->remote_saddr_len = ret;
return 0;
}
#define NO_FLUSH (-42)
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
const unsigned char *remote, int remotelen,
const unsigned char *aeskey, const unsigned char *ecdh_secret)
{
raop_rtp_mirror_t *raop_rtp_mirror;
assert(logger);
assert(callbacks);
raop_rtp_mirror = calloc(1, sizeof(raop_rtp_mirror_t));
if (!raop_rtp_mirror) {
return NULL;
}
raop_rtp_mirror->logger = logger;
raop_rtp_mirror->ntp = ntp;
memcpy(&raop_rtp_mirror->callbacks, callbacks, sizeof(raop_callbacks_t));
raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey, ecdh_secret);
if (!raop_rtp_mirror->buffer) {
free(raop_rtp_mirror);
return NULL;
}
if (raop_rtp_parse_remote(raop_rtp_mirror, remote, remotelen) < 0) {
free(raop_rtp_mirror);
return NULL;
}
raop_rtp_mirror->running = 0;
raop_rtp_mirror->joined = 1;
raop_rtp_mirror->flush = NO_FLUSH;
MUTEX_CREATE(raop_rtp_mirror->run_mutex);
return raop_rtp_mirror;
}
void
raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t streamConnectionID)
{
mirror_buffer_init_aes(raop_rtp_mirror->buffer, streamConnectionID);
}
//#define DUMP_H264
#define RAOP_PACKET_LEN 32768
/**
* Mirror
*/
static THREAD_RETVAL
raop_rtp_mirror_thread(void *arg)
{
raop_rtp_mirror_t *raop_rtp_mirror = arg;
assert(raop_rtp_mirror);
int stream_fd = -1;
unsigned char packet[128];
memset(packet, 0 , 128);
unsigned char* payload = NULL;
unsigned int readstart = 0;
#ifdef DUMP_H264
// C decrypted
FILE* file = fopen("/home/pi/Airplay.h264", "wb");
// Encrypted source file
FILE* file_source = fopen("/home/pi/Airplay.source", "wb");
FILE* file_len = fopen("/home/pi/Airplay.len", "wb");
#endif
while (1) {
fd_set rfds;
struct timeval tv;
int nfds, ret;
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
if (!raop_rtp_mirror->running) {
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
break;
}
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
/* Set timeout value to 5ms */
tv.tv_sec = 0;
tv.tv_usec = 5000;
/* Get the correct nfds value and set rfds */
FD_ZERO(&rfds);
if (stream_fd == -1) {
FD_SET(raop_rtp_mirror->mirror_data_sock, &rfds);
nfds = raop_rtp_mirror->mirror_data_sock+1;
} else {
FD_SET(stream_fd, &rfds);
nfds = stream_fd+1;
}
ret = select(nfds, &rfds, NULL, NULL, &tv);
if (ret == 0) {
/* Timeout happened */
continue;
} else if (ret == -1) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in select");
break;
}
if (stream_fd == -1 && FD_ISSET(raop_rtp_mirror->mirror_data_sock, &rfds)) {
struct sockaddr_storage saddr;
socklen_t saddrlen;
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror accepting client");
saddrlen = sizeof(saddr);
stream_fd = accept(raop_rtp_mirror->mirror_data_sock, (struct sockaddr *)&saddr, &saddrlen);
if (stream_fd == -1) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in accept %d %s", errno, strerror(errno));
break;
}
// We're calling recv for a certain amount of data, so we need a timeout
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 5000;
if (setsockopt(stream_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror could not set stream socket timeout %d %s", errno, strerror(errno));
break;
}
readstart = 0;
}
if (stream_fd != -1 && FD_ISSET(stream_fd, &rfds)) {
// The first 128 bytes are some kind of header for the payload that follows
while (payload == NULL && readstart < 128) {
ret = recv(stream_fd, packet + readstart, 128 - readstart, 0);
if (ret <= 0) break;
readstart = readstart + ret;
}
if (payload == NULL && ret == 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket closed");
FD_CLR(stream_fd, &rfds);
stream_fd = -1;
continue;
} else if (payload == NULL && ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in header recv: %d", errno);
break;
}
int payload_size = byteutils_get_int(packet, 0);
unsigned short payload_type = byteutils_get_short(packet, 4) & 0xff;
unsigned short payload_option = byteutils_get_short(packet, 6);
if (payload == NULL) {
payload = malloc(payload_size);
readstart = 0;
}
while (readstart < payload_size) {
// Payload data
ret = recv(stream_fd, payload + readstart, payload_size - readstart, 0);
if (ret <= 0) break;
readstart = readstart + ret;
}
if (ret == 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket closed");
break;
} else if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in recv: %d", errno);
break;
}
if (payload_type == 0) {
// Normal video data (VCL NAL)
// Conveniently, the video data is already stamped with the remote wall clock time,
// so no additional clock syncing needed. The only thing odd here is that the video
// ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just
// counting micro seconds since last boot.
uint64_t ntp_timestamp_raw = byteutils_get_long(packet, 8);
uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_micro_seconds(ntp_timestamp_raw, false);
uint64_t ntp_timestamp = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote);
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp_mirror->ntp);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror video ntp = %llu, now = %llu, latency = %lld",
ntp_timestamp, ntp_now, ((int64_t) ntp_now) - ((int64_t) ntp_timestamp));
#ifdef DUMP_H264
fwrite(payload, payload_size, 1, file_source);
fwrite(&readstart, sizeof(readstart), 1, file_len);
#endif
// Decrypt data
unsigned char* payload_decrypted = malloc(payload_size);
mirror_buffer_decrypt(raop_rtp_mirror->buffer, payload, payload_decrypted, payload_size);
int nalu_type = payload[4] & 0x1f;
int nalu_size = 0;
int nalus_count = 0;
// It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte
// start code for the NAL Byte-Stream Format.
while (nalu_size < payload_size) {
int nc_len = (payload_decrypted[nalu_size + 0] << 24) | (payload_decrypted[nalu_size + 1] << 16) |
(payload_decrypted[nalu_size + 2] << 8) | (payload_decrypted[nalu_size + 3]);
assert(nc_len > 0);
payload_decrypted[nalu_size + 0] = 0;
payload_decrypted[nalu_size + 1] = 0;
payload_decrypted[nalu_size + 2] = 0;
payload_decrypted[nalu_size + 3] = 1;
nalu_size += nc_len + 4;
nalus_count++;
}
// logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalutype = %d", nalu_type);
// logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalu_size = %d, payloadsize = %d nalus_count = %d",
// nalu_size, payload_size, nalus_count);
#ifdef DUMP_H264
fwrite(payload_decrypted, payload_size, 1, file);
#endif
h264_decode_struct h264_data;
h264_data.data_len = payload_size;
h264_data.data = payload_decrypted;
h264_data.frame_type = 1;
h264_data.pts = ntp_timestamp;
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
free(payload_decrypted);
} else if ((payload_type & 255) == 1) {
// The information in the payload contains an SPS and a PPS NAL
float width_source = byteutils_get_float(packet, 40);
float height_source = byteutils_get_float(packet, 44);
float width = byteutils_get_float(packet, 56);
float height = byteutils_get_float(packet, 60);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror width_source = %f height_source = %f width = %f height = %f",
width_source, height_source, width, height);
// The sps_pps is not encrypted
h264codec_t h264;
h264.version = payload[0];
h264.profile_high = payload[1];
h264.compatibility = payload[2];
h264.level = payload[3];
h264.reserved_6_and_nal = payload[4];
h264.reserved_3_and_sps = payload[5];
h264.sps_size = (short) (((payload[6] & 255) << 8) + (payload[7] & 255));
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror sps size = %d", h264.sps_size);
h264.sequence_parameter_set = malloc(h264.sps_size);
memcpy(h264.sequence_parameter_set, payload + 8, h264.sps_size);
h264.number_of_pps = payload[h264.sps_size + 8];
h264.pps_size = (short) (((payload[h264.sps_size + 9] & 2040) + payload[h264.sps_size + 10]) & 255);
h264.picture_parameter_set = malloc(h264.pps_size);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror pps size = %d", h264.pps_size);
memcpy(h264.picture_parameter_set, payload + h264.sps_size + 11, h264.pps_size);
if (h264.sps_size + h264.pps_size < 102400) {
// Copy the sps and pps into a buffer to hand to the decoder
int sps_pps_len = (h264.sps_size + h264.pps_size) + 8;
unsigned char sps_pps[sps_pps_len];
sps_pps[0] = 0;
sps_pps[1] = 0;
sps_pps[2] = 0;
sps_pps[3] = 1;
memcpy(sps_pps + 4, h264.sequence_parameter_set, h264.sps_size);
sps_pps[h264.sps_size + 4] = 0;
sps_pps[h264.sps_size + 5] = 0;
sps_pps[h264.sps_size + 6] = 0;
sps_pps[h264.sps_size + 7] = 1;
memcpy(sps_pps + h264.sps_size + 8, h264.picture_parameter_set, h264.pps_size);
#ifdef DUMP_H264
fwrite(sps_pps, sps_pps_len, 1, file);
#endif
h264_decode_struct h264_data;
h264_data.data_len = sps_pps_len;
h264_data.data = sps_pps;
h264_data.frame_type = 0;
h264_data.pts = 0;
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
}
free(h264.picture_parameter_set);
free(h264.sequence_parameter_set);
}
free(payload);
payload = NULL;
memset(packet, 0, 128);
readstart = 0;
}
}
/* Close the stream file descriptor */
if (stream_fd != -1) {
closesocket(stream_fd);
}
#ifdef DUMP_H264
fclose(file);
fclose(file_source);
fclose(file_len);
#endif
// Ensure running reflects the actual state
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
raop_rtp_mirror->running = false;
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror exiting TCP thread");
return 0;
}
void
raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport)
{
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror starting mirroring");
int use_ipv6 = 0;
assert(raop_rtp_mirror);
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
if (raop_rtp_mirror->running || !raop_rtp_mirror->joined) {
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
return;
}
if (raop_rtp_mirror->remote_saddr.ss_family == AF_INET6) {
use_ipv6 = 1;
}
use_ipv6 = 0;
if (raop_rtp_init_mirror_sockets(raop_rtp_mirror, use_ipv6) < 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing sockets failed");
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
return;
}
if (mirror_data_lport) *mirror_data_lport = raop_rtp_mirror->mirror_data_lport;
/* Create the thread and initialize running values */
raop_rtp_mirror->running = 1;
raop_rtp_mirror->joined = 0;
THREAD_CREATE(raop_rtp_mirror->thread_mirror, raop_rtp_mirror_thread, raop_rtp_mirror);
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
}
void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror) {
assert(raop_rtp_mirror);
/* Check that we are running and thread is not
* joined (should never be while still running) */
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
if (!raop_rtp_mirror->running || raop_rtp_mirror->joined) {
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
return;
}
raop_rtp_mirror->running = 0;
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
if (raop_rtp_mirror->mirror_data_sock != -1) {
closesocket(raop_rtp_mirror->mirror_data_sock);
raop_rtp_mirror->mirror_data_sock = -1;
}
/* Join the thread */
THREAD_JOIN(raop_rtp_mirror->thread_mirror);
/* Mark thread as joined */
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
raop_rtp_mirror->joined = 1;
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
}
void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror) {
if (raop_rtp_mirror) {
raop_rtp_mirror_stop(raop_rtp_mirror);
MUTEX_DESTROY(raop_rtp_mirror->run_mutex);
mirror_buffer_destroy(raop_rtp_mirror->buffer);
free(raop_rtp_mirror);
}
}
static int
raop_rtp_init_mirror_sockets(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6)
{
int dsock = -1;
unsigned short dport = 0;
assert(raop_rtp_mirror);
dsock = netutils_init_socket(&dport, use_ipv6, 0);
if (dsock == -1) {
goto sockets_cleanup;
}
/* Listen to the data socket if using TCP */
if (listen(dsock, 1) < 0) {
goto sockets_cleanup;
}
/* Set socket descriptors */
raop_rtp_mirror->mirror_data_sock = dsock;
/* Set port values */
raop_rtp_mirror->mirror_data_lport = dport;
return 0;
sockets_cleanup:
if (dsock != -1) closesocket(dsock);
return -1;
}

36
lib/raop_rtp_mirror.h Executable file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef RAOP_RTP_MIRROR_H
#define RAOP_RTP_MIRROR_H
#include <stdint.h>
#include "raop.h"
#include "logger.h"
#include "raop_ntp.h"
typedef struct raop_rtp_mirror_s raop_rtp_mirror_t;
typedef struct h264codec_s h264codec_t;
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
const unsigned char *remote, int remotelen,
const unsigned char *aeskey, const unsigned char *ecdh_secret);
void raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t streamConnectionID);
void raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport);
static int raop_rtp_init_mirror_sockets(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6);
void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror);
void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror);
#endif //RAOP_RTP_MIRROR_H

49
lib/sockets.h Executable file
View File

@@ -0,0 +1,49 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef SOCKETS_H
#define SOCKETS_H
#if defined(WIN32)
typedef int socklen_t;
#ifndef SHUT_RD
# define SHUT_RD SD_RECEIVE
#endif
#ifndef SHUT_WR
# define SHUT_WR SD_SEND
#endif
#ifndef SHUT_RDWR
# define SHUT_RDWR SD_BOTH
#endif
#define SOCKET_GET_ERROR() WSAGetLastError()
#define SOCKET_SET_ERROR(value) WSASetLastError(value)
#define SOCKET_ERRORNAME(name) WSA##name
#define WSAEAGAIN WSAEWOULDBLOCK
#define WSAENOMEM WSA_NOT_ENOUGH_MEMORY
#else
#define closesocket close
#define ioctlsocket ioctl
#define SOCKET_GET_ERROR() (errno)
#define SOCKET_SET_ERROR(value) (errno = (value))
#define SOCKET_ERRORNAME(name) name
#endif
#endif

36
lib/stream.h Executable file
View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 dsafa22, All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef AIRPLAYSERVER_STREAM_H
#define AIRPLAYSERVER_STREAM_H
#include <stdint.h>
typedef struct {
int n_gop_index;
int frame_type;
int n_frame_poc;
unsigned char *data;
int data_len;
unsigned int n_time_stamp;
uint64_t pts;
} h264_decode_struct;
typedef struct {
unsigned char *data;
int data_len;
uint64_t pts;
} aac_decode_struct;
#endif //AIRPLAYSERVER_STREAM_H

66
lib/threads.h Executable file
View File

@@ -0,0 +1,66 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#ifndef THREADS_H
#define THREADS_H
#if defined(WIN32)
#include <windows.h>
#define sleepms(x) Sleep(x)
typedef HANDLE thread_handle_t;
#define THREAD_RETVAL DWORD WINAPI
#define THREAD_CREATE(handle, func, arg) \
handle = CreateThread(NULL, 0, func, arg, 0, NULL)
#define THREAD_JOIN(handle) do { WaitForSingleObject(handle, INFINITE); CloseHandle(handle); } while(0)
typedef HANDLE mutex_handle_t;
#define MUTEX_CREATE(handle) handle = CreateMutex(NULL, FALSE, NULL)
#define MUTEX_LOCK(handle) WaitForSingleObject(handle, INFINITE)
#define MUTEX_UNLOCK(handle) ReleaseMutex(handle)
#define MUTEX_DESTROY(handle) CloseHandle(handle)
#else /* Use pthread library */
#include <pthread.h>
#include <unistd.h>
#define sleepms(x) usleep((x)*1000)
typedef pthread_t thread_handle_t;
#define THREAD_RETVAL void *
#define THREAD_CREATE(handle, func, arg) \
if (pthread_create(&(handle), NULL, func, arg)) handle = 0
#define THREAD_JOIN(handle) pthread_join(handle, NULL)
typedef pthread_mutex_t mutex_handle_t;
typedef pthread_cond_t cond_handle_t;
#define MUTEX_CREATE(handle) pthread_mutex_init(&(handle), NULL)
#define MUTEX_LOCK(handle) pthread_mutex_lock(&(handle))
#define MUTEX_UNLOCK(handle) pthread_mutex_unlock(&(handle))
#define MUTEX_DESTROY(handle) pthread_mutex_destroy(&(handle))
#define COND_CREATE(handle) pthread_cond_init(&(handle), NULL)
#define COND_SIGNAL(handle) pthread_cond_signal(&(handle))
#define COND_DESTROY(handle) pthread_cond_destroy(&(handle))
#endif
#endif /* THREADS_H */

181
lib/utils.c Normal file
View File

@@ -0,0 +1,181 @@
/**
* Copyright (C) 2011-2012 Juho Vähä-Herttua
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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. See the GNU
* Lesser General Public License for more details.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
char *
utils_strsep(char **stringp, const char *delim)
{
char *original;
char *strptr;
if (*stringp == NULL) {
return NULL;
}
original = *stringp;
strptr = strstr(*stringp, delim);
if (strptr == NULL) {
*stringp = NULL;
return original;
}
*strptr = '\0';
*stringp = strptr+strlen(delim);
return original;
}
int
utils_read_file(char **dst, const char *filename)
{
FILE *stream;
int filesize;
char *buffer;
int read_bytes;
/* Open stream for reading */
stream = fopen(filename, "rb");
if (!stream) {
return -1;
}
/* Find out file size */
fseek(stream, 0, SEEK_END);
filesize = ftell(stream);
fseek(stream, 0, SEEK_SET);
/* Allocate one extra byte for zero */
buffer = malloc(filesize+1);
if (!buffer) {
fclose(stream);
return -2;
}
/* Read data in a loop to buffer */
read_bytes = 0;
do {
int ret = fread(buffer+read_bytes, 1,
filesize-read_bytes, stream);
if (ret == 0) {
break;
}
read_bytes += ret;
} while (read_bytes < filesize);
/* Add final null byte and close stream */
buffer[read_bytes] = '\0';
fclose(stream);
/* If read didn't finish, return error */
if (read_bytes != filesize) {
free(buffer);
return -3;
}
/* Return buffer */
*dst = buffer;
return filesize;
}
int
utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen)
{
int i,j;
/* Check that our string is long enough */
if (strlen == 0 || strlen < 2*hwaddrlen+1)
return -1;
/* Convert hardware address to hex string */
for (i=0,j=0; i<hwaddrlen; i++) {
int hi = (hwaddr[i]>>4) & 0x0f;
int lo = hwaddr[i] & 0x0f;
if (hi < 10) str[j++] = '0' + hi;
else str[j++] = 'A' + hi-10;
if (lo < 10) str[j++] = '0' + lo;
else str[j++] = 'A' + lo-10;
}
/* Add string terminator */
str[j++] = '\0';
return j;
}
int
utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen)
{
int i,j;
/* Check that our string is long enough */
if (strlen == 0 || strlen < 2*hwaddrlen+hwaddrlen)
return -1;
/* Convert hardware address to hex string */
for (i=0,j=0; i<hwaddrlen; i++) {
int hi = (hwaddr[i]>>4) & 0x0f;
int lo = hwaddr[i] & 0x0f;
if (hi < 10) str[j++] = '0' + hi;
else str[j++] = 'a' + hi-10;
if (lo < 10) str[j++] = '0' + lo;
else str[j++] = 'a' + lo-10;
str[j++] = ':';
}
/* Add string terminator */
if (j != 0) j--;
str[j++] = '\0';
return j;
}
char *utils_parse_hex(const char *str, int str_len, int *data_len) {
assert(str_len % 2 == 0);
char *data = malloc(str_len / 2);
for (int i = 0; i < (str_len / 2); i++) {
char c_1 = str[i * 2];
if (c_1 >= 97 && c_1 <= 102) {
c_1 -= (97 - 10);
} else if (c_1 >= 65 && c_1 <= 70) {
c_1 -= (65 - 10);
} else if (c_1 >= 48 && c_1 <= 57) {
c_1 -= 48;
} else {
free(data);
return NULL;
}
char c_2 = str[(i * 2) + 1];
if (c_2 >= 97 && c_2 <= 102) {
c_2 -= (97 - 10);
} else if (c_2 >= 65 && c_2 <= 70) {
c_2 -= (65 - 10);
} else if (c_2 >= 48 && c_2 <= 57) {
c_2 -= 48;
} else {
free(data);
return NULL;
}
data[i] = (c_1 << 4) | c_2;
}
*data_len = (str_len / 2);
return data;
}