1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-22 15:39:07 +00:00

Include full clangformat config.

Hopefully stabilizes behaviour between versions.
This commit is contained in:
Thom Wiggers 2019-02-11 10:05:54 +01:00
parent 69e593427a
commit fe46514836
No known key found for this signature in database
GPG Key ID: 001BB0A7CE26E363
5 changed files with 351 additions and 245 deletions

View File

@ -1,7 +1,115 @@
--- ---
Language: Cpp Language: Cpp
BasedOnStyle: LLVM AccessModifierOffset: -2
AllowShortFunctionsOnASingleLine: false AlignAfterOpenBracket: Align
IndentWidth: 4 AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
... ...

View File

@ -26,298 +26,292 @@ THE SOFTWARE.
// *before* randombytes.h is included. Otherwise SYS_getrandom will not be // *before* randombytes.h is included. Otherwise SYS_getrandom will not be
// declared. // declared.
#if defined(__linux__) #if defined(__linux__)
# define _GNU_SOURCE #define _GNU_SOURCE
#endif /* defined(__linux__) */ #endif /* defined(__linux__) */
#include "randombytes.h" #include "randombytes.h"
#if defined(_WIN32) #if defined(_WIN32)
/* Windows */ /* Windows */
# include <windows.h> #include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */
# include <wincrypt.h> /* CryptAcquireContext, CryptGenRandom */ #include <windows.h>
#endif /* defined(_WIN32) */ #endif /* defined(_WIN32) */
#if defined(__linux__) #if defined(__linux__)
/* Linux */ /* Linux */
// We would need to include <linux/random.h>, but not every target has access // We would need to include <linux/random.h>, but not every target has access
// to the linux headers. We only need RNDGETENTCNT, so we instead inline it. // to the linux headers. We only need RNDGETENTCNT, so we instead inline it.
// RNDGETENTCNT is originally defined in `include/uapi/linux/random.h` in the // RNDGETENTCNT is originally defined in `include/uapi/linux/random.h` in the
// linux repo. // linux repo.
# define RNDGETENTCNT 0x80045200 #define RNDGETENTCNT 0x80045200
# include <assert.h> #include <assert.h>
# include <errno.h> #include <errno.h>
# include <fcntl.h> #include <fcntl.h>
# include <poll.h> #include <poll.h>
# include <stdint.h> #include <stdint.h>
# include <stdio.h> #include <stdio.h>
# include <sys/ioctl.h> #include <sys/ioctl.h>
# include <sys/stat.h> #include <sys/stat.h>
# include <sys/syscall.h> #include <sys/syscall.h>
# include <sys/types.h> #include <sys/types.h>
# include <unistd.h> #include <unistd.h>
// We need SSIZE_MAX as the maximum read len from /dev/urandom // We need SSIZE_MAX as the maximum read len from /dev/urandom
# if !defined(SSIZE_MAX) #if !defined(SSIZE_MAX)
# define SSIZE_MAX (SIZE_MAX / 2 - 1) #define SSIZE_MAX (SIZE_MAX / 2 - 1)
# endif /* defined(SSIZE_MAX) */ #endif /* defined(SSIZE_MAX) */
#endif /* defined(__linux__) */ #endif /* defined(__linux__) */
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
/* Dragonfly, FreeBSD, NetBSD, OpenBSD (has arc4random) */ /* Dragonfly, FreeBSD, NetBSD, OpenBSD (has arc4random) */
# include <sys/param.h> #include <sys/param.h>
# if defined(BSD) #if defined(BSD)
# include <stdlib.h> #include <stdlib.h>
# endif #endif
#endif #endif
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
# include <assert.h> #include <assert.h>
# include <emscripten.h> #include <emscripten.h>
# include <errno.h> #include <errno.h>
# include <stdbool.h> #include <stdbool.h>
#endif /* defined(__EMSCRIPTEN__) */ #endif /* defined(__EMSCRIPTEN__) */
#if defined(_WIN32) #if defined(_WIN32)
static int randombytes_win32_randombytes(void* buf, const size_t n) static int randombytes_win32_randombytes(void *buf, const size_t n) {
{ HCRYPTPROV ctx;
HCRYPTPROV ctx; BOOL tmp;
BOOL tmp;
tmp = CryptAcquireContext(&ctx, NULL, NULL, PROV_RSA_FULL, tmp = CryptAcquireContext(&ctx, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT); CRYPT_VERIFYCONTEXT);
if (tmp == FALSE) return -1; if (tmp == FALSE)
return -1;
tmp = CryptGenRandom(ctx, n, (BYTE*) buf); tmp = CryptGenRandom(ctx, n, (BYTE *)buf);
if (tmp == FALSE) return -1; if (tmp == FALSE)
return -1;
tmp = CryptReleaseContext(ctx, 0); tmp = CryptReleaseContext(ctx, 0);
if (tmp == FALSE) return -1; if (tmp == FALSE)
return -1;
return 0; return 0;
} }
#endif /* defined(_WIN32) */ #endif /* defined(_WIN32) */
#if defined(__linux__) && defined(SYS_getrandom) #if defined(__linux__) && defined(SYS_getrandom)
static int randombytes_linux_randombytes_getrandom(void *buf, size_t n) static int randombytes_linux_randombytes_getrandom(void *buf, size_t n) {
{ /* I have thought about using a separate PRF, seeded by getrandom, but
/* I have thought about using a separate PRF, seeded by getrandom, but * it turns out that the performance of getrandom is good enough
* it turns out that the performance of getrandom is good enough * (250 MB/s on my laptop).
* (250 MB/s on my laptop). */
*/ size_t offset = 0, chunk;
size_t offset = 0, chunk; int ret;
int ret; while (n > 0) {
while (n > 0) { /* getrandom does not allow chunks larger than 33554431 */
/* getrandom does not allow chunks larger than 33554431 */ chunk = n <= 33554431 ? n : 33554431;
chunk = n <= 33554431 ? n : 33554431; do {
do { ret = syscall(SYS_getrandom, (char *)buf + offset, chunk, 0);
ret = syscall(SYS_getrandom, (char *)buf + offset, chunk, 0); } while (ret == -1 && errno == EINTR);
} while (ret == -1 && errno == EINTR); if (ret < 0) {
if (ret < 0) { return ret;
return ret; }
} offset += ret;
offset += ret; n -= ret;
n -= ret; }
} assert(n == 0);
assert(n == 0); return 0;
return 0;
} }
#endif /* defined(__linux__) && defined(SYS_getrandom) */ #endif /* defined(__linux__) && defined(SYS_getrandom) */
#if defined(__linux__) && !defined(SYS_getrandom) #if defined(__linux__) && !defined(SYS_getrandom)
static int randombytes_linux_read_entropy_ioctl(int device, int *entropy) static int randombytes_linux_read_entropy_ioctl(int device, int *entropy) {
{ return ioctl(device, RNDGETENTCNT, entropy);
return ioctl(device, RNDGETENTCNT, entropy);
} }
static int randombytes_linux_read_entropy_proc(FILE *stream, int *entropy) static int randombytes_linux_read_entropy_proc(FILE *stream, int *entropy) {
{ int retcode;
int retcode; do {
do { rewind(stream);
rewind(stream); retcode = fscanf(stream, "%d", entropy);
retcode = fscanf(stream, "%d", entropy); } while (retcode != 1 && errno == EINTR);
} while (retcode != 1 && errno == EINTR); if (retcode != 1) {
if (retcode != 1) { return -1;
return -1; }
} return 0;
return 0;
} }
static int randombytes_linux_wait_for_entropy(int device) static int randombytes_linux_wait_for_entropy(int device) {
{ /* We will block on /dev/random, because any increase in the OS' entropy
/* We will block on /dev/random, because any increase in the OS' entropy * level will unblock the request. I use poll here (as does libsodium),
* level will unblock the request. I use poll here (as does libsodium), * because we don't *actually* want to read from the device. */
* because we don't *actually* want to read from the device. */ enum { IOCTL, PROC } strategy = IOCTL;
enum { IOCTL, PROC } strategy = IOCTL; const int bits = 128;
const int bits = 128; struct pollfd pfd;
struct pollfd pfd; int fd;
int fd; FILE *proc_file;
FILE *proc_file; int retcode,
int retcode, retcode_error = 0; // Used as return codes throughout this function retcode_error = 0; // Used as return codes throughout this function
int entropy = 0; int entropy = 0;
/* If the device has enough entropy already, we will want to return early */ /* If the device has enough entropy already, we will want to return early */
retcode = randombytes_linux_read_entropy_ioctl(device, &entropy); retcode = randombytes_linux_read_entropy_ioctl(device, &entropy);
if (retcode != 0 && errno == ENOTTY) { if (retcode != 0 && errno == ENOTTY) {
/* The ioctl call on /dev/urandom has failed due to a ENOTTY (i.e. /* The ioctl call on /dev/urandom has failed due to a ENOTTY (i.e.
* unsupported action). We will fall back to reading from * unsupported action). We will fall back to reading from
* `/proc/sys/kernel/random/entropy_avail`. This is obviously less * `/proc/sys/kernel/random/entropy_avail`. This is obviously less
* ideal, but at this point it seems we have no better option. */ * ideal, but at this point it seems we have no better option. */
strategy = PROC; strategy = PROC;
// Open the entropy count file // Open the entropy count file
proc_file = fopen("/proc/sys/kernel/random/entropy_avail", "r"); proc_file = fopen("/proc/sys/kernel/random/entropy_avail", "r");
} else if (retcode != 0) { } else if (retcode != 0) {
// Unrecoverable ioctl error // Unrecoverable ioctl error
return -1; return -1;
} }
if (entropy >= bits) { if (entropy >= bits) {
return 0; return 0;
} }
do { do {
fd = open("/dev/random", O_RDONLY); fd = open("/dev/random", O_RDONLY);
} while (fd == -1 && errno == EINTR); /* EAGAIN will not occur */ } while (fd == -1 && errno == EINTR); /* EAGAIN will not occur */
if (fd == -1) { if (fd == -1) {
/* Unrecoverable IO error */ /* Unrecoverable IO error */
return -1; return -1;
} }
pfd.fd = fd; pfd.fd = fd;
pfd.events = POLLIN; pfd.events = POLLIN;
for (;;) { for (;;) {
retcode = poll(&pfd, 1, -1); retcode = poll(&pfd, 1, -1);
if (retcode == -1 && (errno == EINTR || errno == EAGAIN)) { if (retcode == -1 && (errno == EINTR || errno == EAGAIN)) {
continue; continue;
} else if (retcode == 1) { } else if (retcode == 1) {
if (strategy == IOCTL) { if (strategy == IOCTL) {
retcode = randombytes_linux_read_entropy_ioctl(device, &entropy); retcode =
} else if (strategy == PROC) { randombytes_linux_read_entropy_ioctl(device, &entropy);
retcode = randombytes_linux_read_entropy_proc(proc_file, &entropy); } else if (strategy == PROC) {
} else { retcode =
return -1; // Unreachable randombytes_linux_read_entropy_proc(proc_file, &entropy);
} } else {
return -1; // Unreachable
if (retcode != 0) { }
// Unrecoverable I/O error
retcode_error = retcode; if (retcode != 0) {
break; // Unrecoverable I/O error
} retcode_error = retcode;
if (entropy >= bits) { break;
break; }
} if (entropy >= bits) {
} else { break;
// Unreachable: poll() should only return -1 or 1 }
retcode_error = -1; } else {
break; // Unreachable: poll() should only return -1 or 1
} retcode_error = -1;
} break;
do { }
retcode = close(fd); }
} while (retcode == -1 && errno == EINTR); do {
if (strategy == PROC) { retcode = close(fd);
do { } while (retcode == -1 && errno == EINTR);
retcode = fclose(proc_file); if (strategy == PROC) {
} while (retcode == -1 && errno == EINTR); do {
} retcode = fclose(proc_file);
if (retcode_error != 0) { } while (retcode == -1 && errno == EINTR);
return retcode_error; }
} if (retcode_error != 0) {
return retcode; return retcode_error;
}
return retcode;
} }
static int randombytes_linux_randombytes_urandom(void *buf, size_t n) {
int fd;
size_t offset = 0, count;
ssize_t tmp;
do {
fd = open("/dev/urandom", O_RDONLY);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return -1;
if (randombytes_linux_wait_for_entropy(fd) == -1)
return -1;
static int randombytes_linux_randombytes_urandom(void *buf, size_t n) while (n > 0) {
{ count = n <= SSIZE_MAX ? n : SSIZE_MAX;
int fd; tmp = read(fd, (char *)buf + offset, count);
size_t offset = 0, count; if (tmp == -1 && (errno == EAGAIN || errno == EINTR)) {
ssize_t tmp; continue;
do { }
fd = open("/dev/urandom", O_RDONLY); if (tmp == -1)
} while (fd == -1 && errno == EINTR); return -1; /* Unrecoverable IO error */
if (fd == -1) return -1; offset += tmp;
if (randombytes_linux_wait_for_entropy(fd) == -1) return -1; n -= tmp;
}
while (n > 0) { assert(n == 0);
count = n <= SSIZE_MAX ? n : SSIZE_MAX; return 0;
tmp = read(fd, (char *)buf + offset, count);
if (tmp == -1 && (errno == EAGAIN || errno == EINTR)) {
continue;
}
if (tmp == -1) return -1; /* Unrecoverable IO error */
offset += tmp;
n -= tmp;
}
assert(n == 0);
return 0;
} }
#endif /* defined(__linux__) && !defined(SYS_getrandom) */ #endif /* defined(__linux__) && !defined(SYS_getrandom) */
#if defined(BSD) #if defined(BSD)
static int randombytes_bsd_randombytes(void *buf, size_t n) static int randombytes_bsd_randombytes(void *buf, size_t n) {
{ arc4random_buf(buf, n);
arc4random_buf(buf, n); return 0;
return 0;
} }
#endif /* defined(BSD) */ #endif /* defined(BSD) */
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
static int randombytes_js_randombytes_nodejs(void *buf, size_t n) { static int randombytes_js_randombytes_nodejs(void *buf, size_t n) {
const int ret = EM_ASM_INT({ const int ret = EM_ASM_INT(
var crypto; {
try { var crypto;
crypto = require('crypto'); try {
} catch (error) { crypto = require('crypto');
return -2; } catch (error) {
} return -2;
try { }
writeArrayToMemory(crypto.randomBytes($1), $0); try {
return 0; writeArrayToMemory(crypto.randomBytes($1), $0);
} catch (error) { return 0;
return -1; } catch (error) {
} return -1;
}, buf, n); }
switch (ret) { },
case 0: buf, n);
return 0; switch (ret) {
case -1: case 0:
errno = EINVAL; return 0;
return -1; case -1:
case -2: errno = EINVAL;
errno = ENOSYS; return -1;
return -1; case -2:
} errno = ENOSYS;
assert(false); // Unreachable return -1;
}
assert(false); // Unreachable
} }
#endif /* defined(__EMSCRIPTEN__) */ #endif /* defined(__EMSCRIPTEN__) */
int randombytes(uint8_t *buf, size_t n) {
int randombytes(uint8_t *buf, size_t n)
{
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
return randombytes_js_randombytes_nodejs(buf, n); return randombytes_js_randombytes_nodejs(buf, n);
#elif defined(__linux__) #elif defined(__linux__)
# if defined(SYS_getrandom) #if defined(SYS_getrandom)
/* Use getrandom system call */ /* Use getrandom system call */
return randombytes_linux_randombytes_getrandom(buf, n); return randombytes_linux_randombytes_getrandom(buf, n);
# else
/* When we have enough entropy, we can read from /dev/urandom */
return randombytes_linux_randombytes_urandom(buf, n);
# endif
#elif defined(BSD)
/* Use arc4random system call */
return randombytes_bsd_randombytes(buf, n);
#elif defined(_WIN32)
/* Use windows API */
return randombytes_win32_randombytes(buf, n);
#else #else
# error "randombytes(...) is not supported on this platform" /* When we have enough entropy, we can read from /dev/urandom */
return randombytes_linux_randombytes_urandom(buf, n);
#endif
#elif defined(BSD)
/* Use arc4random system call */
return randombytes_bsd_randombytes(buf, n);
#elif defined(_WIN32)
/* Use windows API */
return randombytes_win32_randombytes(buf, n);
#else
#error "randombytes(...) is not supported on this platform"
#endif #endif
} }

View File

@ -3,9 +3,9 @@
#include <stdint.h> #include <stdint.h>
#ifdef _WIN32 #ifdef _WIN32
# include <CRTDEFS.H> #include <CRTDEFS.H>
#else #else
# include <unistd.h> #include <unistd.h>
#endif #endif
int randombytes(uint8_t *buf, size_t xlen); int randombytes(uint8_t *buf, size_t xlen);

View File

@ -34,10 +34,10 @@ static int check_canary(const unsigned char *d) {
#define crypto_kem_enc NAMESPACE(crypto_kem_enc) #define crypto_kem_enc NAMESPACE(crypto_kem_enc)
#define crypto_kem_dec NAMESPACE(crypto_kem_dec) #define crypto_kem_dec NAMESPACE(crypto_kem_dec)
#define RETURNS_ZERO(f) \ #define RETURNS_ZERO(f) \
if ((f) != 0) { \ if ((f) != 0) { \
puts(#f " returned non-zero returncode"); \ puts(#f " returned non-zero returncode"); \
return -1; \ return -1; \
} }
static int test_keys(void) { static int test_keys(void) {
@ -107,7 +107,9 @@ static int test_invalid_sk_a(void) {
// Alice uses Bobs response to get her secret key // Alice uses Bobs response to get her secret key
if ((returncode = crypto_kem_dec(key_a, sendb, sk_a)) > 0) { if ((returncode = crypto_kem_dec(key_a, sendb, sk_a)) > 0) {
printf("ERROR failing crypto_kem_dec returned %d instead of negative or zero code\n", returncode); printf("ERROR failing crypto_kem_dec returned %d instead of "
"negative or zero code\n",
returncode);
return -1; return -1;
} }
@ -143,7 +145,9 @@ static int test_invalid_ciphertext(void) {
// Alice uses Bobs response to get her secret key // Alice uses Bobs response to get her secret key
if ((returncode = crypto_kem_dec(key_a, sendb, sk_a)) > 0) { if ((returncode = crypto_kem_dec(key_a, sendb, sk_a)) > 0) {
printf("ERROR crypto_kem_dec should either fail (negative returncode) or succeed (return 0) but returned %d\n", returncode); printf("ERROR crypto_kem_dec should either fail (negative "
"returncode) or succeed (return 0) but returned %d\n",
returncode);
return -1; return -1;
} }

View File

@ -35,13 +35,12 @@ static int check_canary(const unsigned char *d) {
#define crypto_sign NAMESPACE(crypto_sign) #define crypto_sign NAMESPACE(crypto_sign)
#define crypto_sign_open NAMESPACE(crypto_sign_open) #define crypto_sign_open NAMESPACE(crypto_sign_open)
#define RETURNS_ZERO(f) \ #define RETURNS_ZERO(f) \
if ((f) != 0) { \ if ((f) != 0) { \
puts("(f) returned non-zero returncode"); \ puts("(f) returned non-zero returncode"); \
return -1; \ return -1; \
} }
static int test_sign(void) { static int test_sign(void) {
unsigned char pk[CRYPTO_PUBLICKEYBYTES + 16]; unsigned char pk[CRYPTO_PUBLICKEYBYTES + 16];
unsigned char sk[CRYPTO_SECRETKEYBYTES + 16]; unsigned char sk[CRYPTO_SECRETKEYBYTES + 16];
@ -70,7 +69,8 @@ static int test_sign(void) {
// By relying on m == sm we prevent having to allocate CRYPTO_BYTES // By relying on m == sm we prevent having to allocate CRYPTO_BYTES
// twice // twice
if ((returncode = crypto_sign_open(sm + 8, &mlen, sm + 8, smlen, pk + 8)) != 0) { if ((returncode =
crypto_sign_open(sm + 8, &mlen, sm + 8, smlen, pk + 8)) != 0) {
printf("ERROR Signature did not verify correctly!\n"); printf("ERROR Signature did not verify correctly!\n");
if (returncode > 0) { if (returncode > 0) {
puts("ERROR return code should be < 0 on failure"); puts("ERROR return code should be < 0 on failure");