1
0
Fork 0
forked from External/grumble

Throw the code out there. :)

This commit is contained in:
Mikkel Krautz 2010-09-20 15:14:22 +02:00
commit 73ab596ae6
27 changed files with 3565 additions and 0 deletions

6
pkg/cryptstate/Makefile Normal file
View file

@ -0,0 +1,6 @@
include $(GOROOT)/src/Make.inc
TARG = cryptstate
GOFILES = cryptstate.go
include $(GOROOT)/src/Make.pkg

View file

@ -0,0 +1,360 @@
// Grumble - an implementation of Murmur in Go
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package cryptstate
import (
"crypto/aes"
"crypto/rand"
"os"
)
const AESBlockSize = 16
const DecryptHistorySize = 0x100
type CryptState struct {
RawKey [AESBlockSize]byte
EncryptIV [AESBlockSize]byte
DecryptIV [AESBlockSize]byte
decryptHistory [DecryptHistorySize]byte
Good int
Late int
Lost int
Resync int
RemoteGood int
RemoteLate int
RemoteLost int
RemoteResync int
cipher *aes.Cipher
}
func New() (cs *CryptState, err os.Error) {
cs = new(CryptState)
for i := 0; i < DecryptHistorySize; i++ {
cs.decryptHistory[i] = 0
}
return
}
func (cs *CryptState) GenerateKey() (err os.Error) {
rand.Read(cs.RawKey[0:])
rand.Read(cs.EncryptIV[0:])
rand.Read(cs.DecryptIV[0:])
cs.cipher, err = aes.NewCipher(cs.RawKey[0:])
if err != nil {
return
}
return
}
func (cs *CryptState) SetKey(key []byte, eiv []byte, div[]byte) (err os.Error) {
if copy(cs.RawKey[0:], key[0:]) != AESBlockSize {
err = os.NewError("Unable to copy key")
return
}
if copy(cs.EncryptIV[0:], eiv[0:]) != AESBlockSize {
err = os.NewError("Unable to copy EIV")
return
}
if copy(cs.DecryptIV[0:], div[0:]) != AESBlockSize {
err = os.NewError("Unable to copy DIV")
return
}
cs.cipher, err = aes.NewCipher(cs.RawKey[0:])
if err != nil {
return
}
return
}
func (cs *CryptState) Decrypt(src, dst []byte) (err os.Error) {
if len(src) < 4 {
err = os.NewError("Crypted length too short to decrypt")
return
}
plain_len := len(src) - 4
if len(dst) != plain_len {
err = os.NewError("plain_len and src len mismatch")
return
}
var saveiv [AESBlockSize]byte
var tag [AESBlockSize]byte
var ivbyte byte
var restore bool
lost := 0
late := 0
ivbyte = src[0]
restore = false
if copy(saveiv[0:], cs.DecryptIV[0:]) != AESBlockSize {
err = os.NewError("Copy failed")
return
}
if byte(cs.DecryptIV[0] + 1) == ivbyte {
// In order as expected
if ivbyte > cs.DecryptIV[0] {
cs.DecryptIV[0] = ivbyte
} else if ivbyte < cs.DecryptIV[0] {
cs.DecryptIV[0] = ivbyte
for i := 1; i < AESBlockSize; i++ {
cs.DecryptIV[i] += 1
if cs.DecryptIV[i] > 0 {
break
}
}
} else {
err = os.NewError("invalid ivbyte")
}
} else {
// Out of order or repeat
var diff int
diff = int(ivbyte - cs.DecryptIV[0])
if diff > 128 {
diff = diff - 256
} else if diff < -128 {
diff = diff + 256
}
if ivbyte < cs.DecryptIV[0] && diff > -30 && diff < 0 {
// Late packet, but no wraparound
late = 1
lost = -1
cs.DecryptIV[0] = ivbyte
restore = true
} else if ivbyte > cs.DecryptIV[0] && diff > -30 && diff < 0 {
// Last was 0x02, here comes 0xff from last round
late = 1
lost = -1
cs.DecryptIV[0] = ivbyte
for i := 1; i < AESBlockSize; i++ {
cs.DecryptIV[0] -= 1
if cs.DecryptIV[0] > 0 {
break
}
}
restore = true
} else if ivbyte > cs.DecryptIV[0] && diff > 0 {
// Lost a few packets, but beyond that we're good.
lost = int(ivbyte - cs.DecryptIV[0] - 1)
cs.DecryptIV[0] = ivbyte
} else if ivbyte < cs.DecryptIV[0] && diff > 0 {
// Lost a few packets, and wrapped around
lost = int(256 - int(cs.DecryptIV[0]) + int(ivbyte) - 1)
cs.DecryptIV[0] = ivbyte
for i := 1; i < AESBlockSize; i++ {
cs.DecryptIV[0] += 1
if cs.DecryptIV[0] > 0 {
break
}
}
} else {
err = os.NewError("No matching ivbyte")
return
}
if cs.decryptHistory[cs.DecryptIV[0]] == cs.DecryptIV[0] {
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
err = os.NewError("Failed to copy AESBlockSize bytes")
return
}
}
}
cs.OCBDecrypt(src[4:], dst[0:], cs.DecryptIV[0:], tag[0:])
for i := 0; i < 3; i++ {
if tag[i] != src[i+1] {
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
err = os.NewError("Error while trying to recover from error")
return
}
err = os.NewError("tag mismatch")
return
}
}
cs.decryptHistory[cs.DecryptIV[0]] = cs.DecryptIV[0]
if restore {
if copy(cs.DecryptIV[0:], saveiv[0:]) != AESBlockSize {
err = os.NewError("Error while trying to recover IV")
return
}
}
cs.Good += 1
cs.Late += late
cs.Lost += lost
// restart timer
return
}
func (cs *CryptState) Encrypt(src, dst []byte) {
var tag [AESBlockSize]byte
// First, increase our IV
for i := 0; i < AESBlockSize; i++ {
cs.EncryptIV[i] += 1;
if cs.EncryptIV[i] > 0 {
break;
}
}
cs.OCBEncrypt(src, dst[4:], cs.EncryptIV[0:], tag[0:])
dst[0] = cs.EncryptIV[0]
dst[1] = tag[0];
dst[2] = tag[1];
dst[3] = tag[2];
return
}
func zeros(block []byte) {
for i := 0; i < AESBlockSize; i++ {
block[i] = 0
}
}
func xor(dst []byte, a []byte, b []byte) {
for i := 0; i < AESBlockSize; i++ {
dst[i] = a[i] ^ b[i]
}
}
func times2(block []byte) {
carry := (block[0] >> 7) & 0x1
for i := 0; i < AESBlockSize-1; i++ {
block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
}
block[AESBlockSize-1] = (block[AESBlockSize-1] << 1) ^ (carry * 135)
}
func times3(block []byte) {
carry := (block[0] >> 7) & 0x1;
for i := 0; i < AESBlockSize-1; i++ {
block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
}
block[AESBlockSize-1] ^= ((block[AESBlockSize-1] << 1) ^ (carry * 135))
}
func (cs *CryptState) OCBEncrypt(src []byte, dst []byte, nonce []byte, tag []byte) (err os.Error) {
var delta [AESBlockSize]byte
var checksum [AESBlockSize]byte
var tmp [AESBlockSize]byte
var pad [AESBlockSize]byte
off := 0
cs.cipher.Encrypt(cs.EncryptIV[0:], delta[0:])
zeros(checksum[0:])
remain := len(src)
for remain > AESBlockSize {
times2(delta[0:])
xor(tmp[0:], delta[0:], src[off:off+AESBlockSize])
cs.cipher.Encrypt(tmp[0:], tmp[0:])
xor(dst[off:off+AESBlockSize], delta[0:], tmp[0:])
xor(checksum[0:], checksum[0:], src[off:off+AESBlockSize])
remain -= AESBlockSize
off += AESBlockSize
}
times2(delta[0:])
zeros(tmp[0:])
num := remain * 8
tmp[AESBlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
tmp[AESBlockSize-1] = uint8(num & 0xff)
xor(tmp[0:], tmp[0:], delta[0:])
cs.cipher.Encrypt(tmp[0:], pad[0:])
copied := copy(tmp[0:], src[off:])
if copied != remain {
err = os.NewError("Copy failed")
return
}
if copy(tmp[copied:], pad[copied:]) != (AESBlockSize-remain) {
err = os.NewError("Copy failed")
return
}
xor(checksum[0:], checksum[0:], tmp[0:])
xor(tmp[0:], pad[0:], tmp[0:])
if copy(dst[off:], tmp[0:]) != remain {
err = os.NewError("Copy failed")
return
}
times3(delta[0:])
xor(tmp[0:], delta[0:], checksum[0:])
cs.cipher.Encrypt(tmp[0:], tag[0:])
return
}
func (cs *CryptState) OCBDecrypt(encrypted []byte, plain []byte, nonce []byte, tag []byte) (err os.Error) {
var checksum [AESBlockSize]byte
var delta [AESBlockSize]byte
var tmp [AESBlockSize]byte
var pad [AESBlockSize]byte
off := 0
cs.cipher.Encrypt(nonce[0:], delta[0:])
zeros(checksum[0:])
remain := len(encrypted)
for remain > AESBlockSize {
times2(delta[0:])
xor(tmp[0:], delta[0:], encrypted[off:off+AESBlockSize])
cs.cipher.Decrypt(tmp[0:], tmp[0:])
xor(plain[off:off+AESBlockSize], delta[0:], tmp[0:])
xor(checksum[0:], checksum[0:], plain[off:off+AESBlockSize])
off += AESBlockSize
remain -= AESBlockSize
}
times2(delta[0:])
zeros(tmp[0:])
num := remain * 8
tmp[AESBlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
tmp[AESBlockSize-1] = uint8(num & 0xff)
xor(tmp[0:], tmp[0:], delta[0:])
cs.cipher.Encrypt(tmp[0:], pad[0:])
for i := 0; i < AESBlockSize; i++ {
tmp[i] = 0
}
copied := copy(tmp[0:remain], encrypted[off:off+remain])
if copied != remain {
err = os.NewError("Copy failed")
return
}
xor(tmp[0:], tmp[0:], pad[0:])
xor(checksum[0:], checksum[0:], tmp[0:])
copied = copy(plain[off:off+remain], tmp[0:remain])
if copied != remain {
err = os.NewError("Copy failed")
return
}
times3(delta[0:])
xor(tmp[0:], delta[0:], checksum[0:])
cs.cipher.Encrypt(tmp[0:], tag[0:])
return
}

View file

@ -0,0 +1,147 @@
package cryptstate
import (
"testing"
)
func BlockCompare(a []byte, b []byte) (match bool) {
if len(a) != len(b) {
return
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return
}
}
match = true
return
}
func TestTimes2(t *testing.T) {
msg := [AESBlockSize]byte{
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
expected := [AESBlockSize]byte{
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7b,
}
times2(msg[0:])
if BlockCompare(msg[0:], expected[0:]) == false {
t.Errorf("times2 produces invalid output: %v, expected: %v", msg, expected)
}
}
func TestTimes3(t *testing.T) {
msg := [AESBlockSize]byte{
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
expected := [AESBlockSize]byte {
0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
}
times3(msg[0:])
if BlockCompare(msg[0:], expected[0:]) == false {
t.Errorf("times3 produces invalid output: %v, expected: %v", msg, expected)
}
}
func TestZeros(t *testing.T) {
var msg [AESBlockSize]byte
zeros(msg[0:])
for i := 0; i < len(msg); i++ {
if msg[i] != 0 {
t.Errorf("zeros does not zero slice.")
}
}
}
func TestXor(t *testing.T) {
msg := [AESBlockSize]byte{
0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
var out [AESBlockSize]byte
xor(out[0:], msg[0:], msg[0:])
for i := 0; i < len(out); i++ {
if out[i] != 0 {
t.Errorf("XOR broken")
}
}
}
func TestEncrypt(t *testing.T) {
msg := [15]byte {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
}
key := [AESBlockSize]byte {
0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32,
}
eiv := [AESBlockSize]byte {
0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
div := [AESBlockSize]byte {
0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07,
}
expected := [19]byte {
0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4,
}
expected_eiv := [AESBlockSize]byte {
0x1f, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
cs, err := New()
if err != nil {
t.Errorf("%v", err)
}
out := make([]byte, 19)
cs.SetKey(key[0:], eiv[0:], div[0:])
cs.Encrypt(msg[0:], out[0:])
if BlockCompare(out[0:], expected[0:]) == false {
t.Errorf("Mismatch in output")
}
if BlockCompare(cs.EncryptIV[0:], expected_eiv[0:]) == false {
t.Errorf("EIV mismatch")
}
}
func TestDecrypt(t *testing.T) {
key := [AESBlockSize]byte {
0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32,
}
eiv := [AESBlockSize]byte {
0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
div := [AESBlockSize]byte {
0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07,
}
crypted := [19]byte {
0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4,
}
expected := [15]byte {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
}
post_div := [AESBlockSize]byte {
0x1f, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a,
}
cs, err := New()
if err != nil {
t.Errorf("%v", err)
}
out := make([]byte, 15)
cs.SetKey(key[0:], div[0:], eiv[0:])
cs.Decrypt(crypted[0:], out[0:])
if BlockCompare(out[0:], expected[0:]) == false {
t.Errorf("Mismatch in output")
}
if BlockCompare(cs.DecryptIV[0:], post_div[0:]) == false {
t.Errorf("Mismatch in DIV")
}
}

View file

@ -0,0 +1,322 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers 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 FOUNDATION 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.
*/
/*
* This code implements OCB-AES128.
* In the US, OCB is covered by patents. The inventor has given a license
* to all programs distributed under the GPL.
* Mumble is BSD (revised) licensed, meaning you can use the code in a
* closed-source program. If you do, you'll have to either replace
* OCB with something else or get yourself a license.
*/
#include "CryptState.h"
#include <openssl/rand.h>
#include <stdint.h>
#include <string.h>
namespace MumbleClient {
CryptState::CryptState() {
for (int i = 0; i < 0x100; i++)
decrypt_history[i] = 0;
bInit = false;
uiGood = uiLate = uiLost = uiResync = 0;
uiRemoteGood = uiRemoteLate = uiRemoteLost = uiRemoteResync = 0;
}
bool CryptState::isValid() const {
return bInit;
}
void CryptState::genKey() {
RAND_bytes(raw_key, AES_BLOCK_SIZE);
RAND_bytes(encrypt_iv, AES_BLOCK_SIZE);
RAND_bytes(decrypt_iv, AES_BLOCK_SIZE);
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
bInit = true;
}
void CryptState::setKey(const unsigned char* rkey, const unsigned char* eiv, const unsigned char* div) {
memcpy(raw_key, rkey, AES_BLOCK_SIZE);
memcpy(encrypt_iv, eiv, AES_BLOCK_SIZE);
memcpy(decrypt_iv, div, AES_BLOCK_SIZE);
AES_set_encrypt_key(raw_key, 128, &encrypt_key);
AES_set_decrypt_key(raw_key, 128, &decrypt_key);
bInit = true;
}
void CryptState::setDecryptIV(const unsigned char* iv) {
memcpy(decrypt_iv, iv, AES_BLOCK_SIZE);
}
const unsigned char* CryptState::getEncryptIV() const {
return encrypt_iv;
}
void CryptState::encrypt(const unsigned char* source, unsigned char* dst, unsigned int plain_length) {
unsigned char tag[AES_BLOCK_SIZE];
// First, increase our IV.
for (int i = 0; i < AES_BLOCK_SIZE; i++)
if (++encrypt_iv[i])
break;
ocb_encrypt(source, dst+4, plain_length, encrypt_iv, tag);
dst[0] = encrypt_iv[0];
dst[1] = tag[0];
dst[2] = tag[1];
dst[3] = tag[2];
}
bool CryptState::decrypt(const unsigned char* source, unsigned char* dst, unsigned int crypted_length) {
if (crypted_length < 4)
return false;
unsigned int plain_length = crypted_length - 4;
unsigned char saveiv[AES_BLOCK_SIZE];
unsigned char ivbyte = source[0];
bool restore = false;
unsigned char tag[AES_BLOCK_SIZE];
int lost = 0;
int late = 0;
memcpy(saveiv, decrypt_iv, AES_BLOCK_SIZE);
if (((decrypt_iv[0] + 1) & 0xFF) == ivbyte) {
// In order as expected.
if (ivbyte > decrypt_iv[0]) {
decrypt_iv[0] = ivbyte;
} else if (ivbyte < decrypt_iv[0]) {
decrypt_iv[0] = ivbyte;
for (int i = 1;i < AES_BLOCK_SIZE; i++)
if (++decrypt_iv[i])
break;
} else {
return false;
}
} else {
// This is either out of order or a repeat.
int diff = ivbyte - decrypt_iv[0];
if (diff > 128)
diff = diff-256;
else if (diff < -128)
diff = diff+256;
if ((ivbyte < decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
// Late packet, but no wraparound.
late = 1;
lost = -1;
decrypt_iv[0] = ivbyte;
restore = true;
} else if ((ivbyte > decrypt_iv[0]) && (diff > -30) && (diff < 0)) {
// Last was 0x02, here comes 0xff from last round
late = 1;
lost = -1;
decrypt_iv[0] = ivbyte;
for (int i = 1; i < AES_BLOCK_SIZE; i++)
if (decrypt_iv[i]--)
break;
restore = true;
} else if ((ivbyte > decrypt_iv[0]) && (diff > 0)) {
// Lost a few packets, but beyond that we're good.
lost = ivbyte - decrypt_iv[0] - 1;
decrypt_iv[0] = ivbyte;
} else if ((ivbyte < decrypt_iv[0]) && (diff > 0)) {
// Lost a few packets, and wrapped around
lost = 256 - decrypt_iv[0] + ivbyte - 1;
decrypt_iv[0] = ivbyte;
for (int i = 1; i < AES_BLOCK_SIZE; i++)
if (++decrypt_iv[i])
break;
} else {
return false;
}
if (decrypt_history[decrypt_iv[0]] == decrypt_iv[1]) {
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
return false;
}
}
ocb_decrypt(source + 4, dst, plain_length, decrypt_iv, tag);
if (memcmp(tag, source + 1, 3) != 0) {
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
return false;
}
decrypt_history[decrypt_iv[0]] = decrypt_iv[1];
if (restore)
memcpy(decrypt_iv, saveiv, AES_BLOCK_SIZE);
uiGood++;
uiLate += late;
uiLost += lost;
return true;
}
#if defined(__LP64__)
#define BLOCKSIZE 2
#define SHIFTBITS 63
typedef uint64_t subblock;
#ifdef __x86_64__
static inline uint64_t SWAP64(register uint64_t __in) { register uint64_t __out; __asm__("bswap %q0" : "=r"(__out) : "0"(__in)); return __out; }
#else
#define SWAP64(x) ((static_cast<uint64_t>(x) << 56) | \
((static_cast<uint64_t>(x) << 40) & 0xff000000000000ULL) | \
((static_cast<uint64_t>(x) << 24) & 0xff0000000000ULL) | \
((static_cast<uint64_t>(x) << 8) & 0xff00000000ULL) | \
((static_cast<uint64_t>(x) >> 8) & 0xff000000ULL) | \
((static_cast<uint64_t>(x) >> 24) & 0xff0000ULL) | \
((static_cast<uint64_t>(x) >> 40) & 0xff00ULL) | \
((static_cast<uint64_t>(x) >> 56)))
#endif
#define SWAPPED(x) SWAP64(x)
#else
#define BLOCKSIZE 4
#define SHIFTBITS 31
typedef uint32_t subblock;
#define SWAPPED(x) htonl(x)
#endif
typedef subblock keyblock[BLOCKSIZE];
#define HIGHBIT (1<<SHIFTBITS);
static void inline XOR(subblock* dst, const subblock* a, const subblock* b) {
for (int i = 0; i < BLOCKSIZE; i++)
dst[i] = a[i] ^ b[i];
}
static void inline S2(subblock* block) {
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
for (int i = 0; i < BLOCKSIZE - 1; i++)
block[i] = SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
block[BLOCKSIZE - 1] = SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^(carry * 0x87));
}
static void inline S3(subblock* block) {
subblock carry = SWAPPED(block[0]) >> SHIFTBITS;
for (int i = 0; i < BLOCKSIZE - 1; i++)
block[i] ^= SWAPPED((SWAPPED(block[i]) << 1) | (SWAPPED(block[i + 1]) >> SHIFTBITS));
block[BLOCKSIZE - 1] ^= SWAPPED((SWAPPED(block[BLOCKSIZE - 1]) << 1) ^(carry * 0x87));
}
static void inline ZERO(keyblock &block) {
for (int i = 0; i < BLOCKSIZE; i++)
block[i] = 0;
}
#define AESencrypt(src,dst,key) AES_encrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
#define AESdecrypt(src,dst,key) AES_decrypt(reinterpret_cast<const unsigned char *>(src),reinterpret_cast<unsigned char *>(dst), key);
void CryptState::ocb_encrypt(const unsigned char* plain, unsigned char* encrypted, unsigned int len, const unsigned char* nonce, unsigned char* tag) {
keyblock checksum, delta, tmp, pad;
// Initialize
AESencrypt(nonce, delta, &encrypt_key);
ZERO(checksum);
while (len > AES_BLOCK_SIZE) {
S2(delta);
XOR(tmp, delta, reinterpret_cast<const subblock *>(plain));
AESencrypt(tmp, tmp, &encrypt_key);
XOR(reinterpret_cast<subblock *>(encrypted), delta, tmp);
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
len -= AES_BLOCK_SIZE;
plain += AES_BLOCK_SIZE;
encrypted += AES_BLOCK_SIZE;
}
S2(delta);
ZERO(tmp);
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
XOR(tmp, tmp, delta);
AESencrypt(tmp, pad, &encrypt_key);
memcpy(tmp, plain, len);
memcpy(reinterpret_cast<unsigned char *>(tmp) + len, reinterpret_cast<const unsigned char *>(pad) + len, AES_BLOCK_SIZE - len);
XOR(checksum, checksum, tmp);
XOR(tmp, pad, tmp);
memcpy(encrypted, tmp, len);
S3(delta);
XOR(tmp, delta, checksum);
AESencrypt(tmp, tag, &encrypt_key);
}
void CryptState::ocb_decrypt(const unsigned char* encrypted, unsigned char* plain, unsigned int len, const unsigned char* nonce, unsigned char* tag) {
keyblock checksum, delta, tmp, pad;
// Initialize
AESencrypt(nonce, delta, &encrypt_key);
ZERO(checksum);
while (len > AES_BLOCK_SIZE) {
S2(delta);
XOR(tmp, delta, reinterpret_cast<const subblock *>(encrypted));
AESdecrypt(tmp, tmp, &decrypt_key);
XOR(reinterpret_cast<subblock *>(plain), delta, tmp);
XOR(checksum, checksum, reinterpret_cast<const subblock *>(plain));
len -= AES_BLOCK_SIZE;
plain += AES_BLOCK_SIZE;
encrypted += AES_BLOCK_SIZE;
}
S2(delta);
ZERO(tmp);
tmp[BLOCKSIZE - 1] = SWAPPED(len * 8);
XOR(tmp, tmp, delta);
AESencrypt(tmp, pad, &encrypt_key);
memset(tmp, 0, AES_BLOCK_SIZE);
memcpy(tmp, encrypted, len);
XOR(tmp, tmp, pad);
XOR(checksum, checksum, tmp);
memcpy(plain, tmp, len);
S3(delta);
XOR(tmp, delta, checksum);
AESencrypt(tmp, tag, &encrypt_key);
}
} // end namespace MumbleClient

View file

@ -0,0 +1,77 @@
/* Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the Mumble Developers 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 FOUNDATION 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.
*/
#ifndef _CRYPTSTATE_H
#define _CRYPTSTATE_H
#include <openssl/aes.h>
namespace MumbleClient {
class CryptState {
public:
unsigned char raw_key[AES_BLOCK_SIZE];
unsigned char encrypt_iv[AES_BLOCK_SIZE];
unsigned char decrypt_iv[AES_BLOCK_SIZE];
unsigned char decrypt_history[0x100];
unsigned int uiGood;
unsigned int uiLate;
unsigned int uiLost;
unsigned int uiResync;
unsigned int uiRemoteGood;
unsigned int uiRemoteLate;
unsigned int uiRemoteLost;
unsigned int uiRemoteResync;
AES_KEY encrypt_key;
AES_KEY decrypt_key;
bool bInit;
public:
CryptState();
bool isValid() const;
void genKey();
void setKey(const unsigned char* rkey, const unsigned char* eiv, const unsigned char* div);
void setDecryptIV(const unsigned char* iv);
const unsigned char* getEncryptIV() const;
void ocb_encrypt(const unsigned char* plain, unsigned char* encrypted, unsigned int len, const unsigned char* nonce, unsigned char* tag);
void ocb_decrypt(const unsigned char* encrypted, unsigned char* plain, unsigned int len, const unsigned char* nonce, unsigned char* tag);
bool decrypt(const unsigned char* source, unsigned char* dst, unsigned int crypted_length);
void encrypt(const unsigned char* source, unsigned char* dst, unsigned int plain_length);
};
} // end namespace MumbleClient
#endif

View file

@ -0,0 +1,9 @@
.PHONY: default
default:
g++ test.cpp CryptState.cpp -o test -lcrypto
g++ test2.cpp CryptState.cpp -o test2 -lcrypto
g++ test3.cpp CryptState.cpp -o test3 -lcrypto
.PHONY: clean
clean:
rm -f test test2 test3

View file

@ -0,0 +1 @@
C++ code for generating some of the test vectors used in cryptstate_test.go

View file

@ -0,0 +1,28 @@
#include "CryptState.h"
#include <stdio.h>
unsigned char msg[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
printf("unsigned char %s[] = { ", name);
for (int i = 0; i < len; i++) {
printf("0x%.2x, ", bytes[i]);
}
printf("}\n");
}
int main(int argc, char *argv[]) {
MumbleClient::CryptState cs;
cs.genKey();
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
unsigned char buf[19];
cs.encrypt(msg, &buf[0], 15);
DumpBytes(buf, 19, "crypted");
}

View file

@ -0,0 +1,36 @@
#include "CryptState.h"
#include <stdio.h>
unsigned char msg[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
unsigned char rawkey[] = { 0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32, };
unsigned char encrypt_iv[] = { 0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a, };
unsigned char decrypt_iv[] = { 0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07, };
unsigned char crypted[] = { 0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4, };
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
printf("unsigned char %s[] = { ", name);
for (int i = 0; i < len; i++) {
printf("0x%.2x, ", bytes[i]);
}
printf("}\n");
}
int main(int argc, char *argv[]) {
MumbleClient::CryptState cs;
// cs.genKey();
cs.setKey(rawkey, encrypt_iv, decrypt_iv);
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
unsigned char buf[19];
cs.encrypt(msg, &buf[0], 15);
DumpBytes(buf, 19, "crypted");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "post_eiv");
}

View file

@ -0,0 +1,34 @@
#include "CryptState.h"
#include <stdio.h>
unsigned char msg[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
unsigned char rawkey[] = { 0x96, 0x8b, 0x1b, 0x0c, 0x53, 0x1e, 0x1f, 0x80, 0xa6, 0x1d, 0xcb, 0x27, 0x94, 0x09, 0x6f, 0x32, };
unsigned char encrypt_iv[] = { 0x1e, 0x2a, 0x9b, 0xd0, 0x2d, 0xa6, 0x8e, 0x46, 0x26, 0x85, 0x83, 0xe9, 0x14, 0x2a, 0xff, 0x2a, };
unsigned char decrypt_iv[] = { 0x73, 0x99, 0x9d, 0xa2, 0x03, 0x70, 0x00, 0x96, 0xef, 0x55, 0x06, 0x7a, 0x8b, 0xbe, 0x00, 0x07, };
unsigned char crypted[] = { 0x1f, 0xfc, 0xdd, 0xb4, 0x68, 0x13, 0x68, 0xb7, 0x92, 0x67, 0xca, 0x2d, 0xba, 0xb7, 0x0d, 0x44, 0xdf, 0x32, 0xd4, };
static void DumpBytes(unsigned char *bytes, unsigned int len, const char *name) {
printf("unsigned char %s[] = { ", name);
for (int i = 0; i < len; i++) {
printf("0x%.2x, ", bytes[i]);
}
printf("}\n");
}
int main(int argc, char *argv[]) {
MumbleClient::CryptState cs;
cs.setKey(rawkey, decrypt_iv, encrypt_iv);
DumpBytes(cs.raw_key, AES_BLOCK_SIZE, "rawkey");
DumpBytes(cs.encrypt_iv, AES_BLOCK_SIZE, "encrypt_iv");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "decrypt_iv");
unsigned char buf[15];
cs.decrypt(crypted, &buf[0], 19);
DumpBytes(buf, 15, "plain");
DumpBytes(cs.decrypt_iv, AES_BLOCK_SIZE, "post_div");
}