Skip to content
This repository has been archived by the owner on Apr 13, 2019. It is now read-only.

Commit

Permalink
Update certificate layout to store crypto fields as records
Browse files Browse the repository at this point in the history
Allows more flexibility in case crypto algorithms need to be updated
at a future date - changing the size of crypto buffers will no longer
require a new layout version, and as a result it will be easier to
implement such a change in a backwards-compatible manner.
  • Loading branch information
erayd committed May 27, 2015
1 parent bf1f1eb commit 032e418
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 87 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* Allow searches to exclude NOSIGN records
* Add interface to quickly get or create a record buffer of minimum length
* Add interface to bulk-set additional flags for an entire section
* Alter certificate layout to store crypto-related buffers as records

###Bugfixes
* Don't try to validate a NULL pointer when checking certificates
Expand Down
32 changes: 19 additions & 13 deletions src/libec/cert/cert.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ ec_cert_t *ec_cert_create(time_t valid_from, time_t valid_until) {
ec_cert_t *c = talloc_zero(NULL, ec_cert_t);
if(!c)
ec_err_r(ENOMEM, NULL);
c->pk = talloc_size(c, crypto_sign_PUBLICKEYBYTES);
c->sk = talloc_size(c, crypto_sign_SECRETKEYBYTES);
c->salt = talloc_size(c, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
c->pk = ec_record_buf(c, "_cert", "pk", crypto_sign_PUBLICKEYBYTES, 0);
c->sk = ec_record_buf(c, "_cert", "sk", crypto_sign_SECRETKEYBYTES, EC_RECORD_NOSIGN);
c->salt = ec_record_buf(c, "_cert", "salt", crypto_pwhash_scryptsalsa208sha256_SALTBYTES,
EC_RECORD_NOSIGN);
if(!c->pk || !c->sk || !c->salt) {
talloc_free(c);
return NULL;
Expand Down Expand Up @@ -71,19 +72,26 @@ void ec_cert_destroy(ec_cert_t *c) {
* Strip data from a certificate
*/
void ec_cert_strip(ec_cert_t *c, int what) {
ec_record_t *sk = ec_record_match(ec_cert_records(c), "_cert", 0, "sk", NULL, 0);
ec_record_t *salt = ec_record_match(ec_cert_records(c), "_cert", 0, "salt", NULL, 0);
ec_record_t *signer_id = ec_record_match(ec_cert_records(c), "_cert", 0, "signer_id", NULL, 0);
ec_record_t *signature = ec_record_match(ec_cert_records(c), "_cert", 0, "signature", NULL, 0);

//strip secret key
if(what & EC_STRIP_SECRET) {
sodium_munlock(c->sk, crypto_sign_SECRETKEYBYTES);
talloc_free(c->sk);
c->sk = NULL;
c->salt = NULL;
ec_record_destroy(ec_record_remove(c, sk));
ec_record_destroy(ec_record_remove(c, salt));
}

//strip NOSIGN records
//strip NOSIGN records other than secret & signature
if(what & EC_STRIP_RECORD) {
ec_record_t **ptr = &c->records;
ec_record_t *r = *ptr;
for(; r; r = *ptr) {
if(r->flags & EC_RECORD_NOSIGN) {
if((r->flags & EC_RECORD_NOSIGN) && r != sk && r != salt && r != signature) {
*ptr = r->next;
ec_record_destroy(r);
}
Expand All @@ -94,10 +102,10 @@ void ec_cert_strip(ec_cert_t *c, int what) {

//strip signer & signature
if(what & EC_STRIP_SIGN) {
talloc_free(c->signer_id);
c->signer_id = NULL;
talloc_free(c->signature);
c->signature = NULL;
ec_record_destroy(ec_record_remove(c, signer_id));
ec_record_destroy(ec_record_remove(c, signature));
}
}

Expand All @@ -118,9 +126,6 @@ ec_err_t ec_cert_hash(unsigned char *hash, ec_cert_t *c) {
crypto_generichash_update(&state, (unsigned char*)&sign_flags, sizeof(sign_flags));
crypto_generichash_update(&state, (unsigned char*)&c->valid_from, sizeof(c->valid_from));
crypto_generichash_update(&state, (unsigned char*)&c->valid_until, sizeof(c->valid_until));
//pk & signer pk
crypto_generichash_update(&state, c->pk, crypto_sign_PUBLICKEYBYTES);
crypto_generichash_update(&state, c->signer_id, crypto_sign_PUBLICKEYBYTES);
//records
for(ec_record_t *r = ec_cert_records(c); r; r = r->next) {
//don't hash NOSIGN records
Expand Down Expand Up @@ -155,15 +160,16 @@ ec_err_t ec_cert_sign(ec_cert_t *c, ec_cert_t *signer) {
c->valid_until = signer->valid_until;

//add signer data
if(!(c->signer_id = talloc_memdup(c, ec_cert_id(signer), EC_CERT_ID_BYTES)))
if(!(c->signer_id = ec_record_buf(c, "_cert", "signer_id", EC_CERT_ID_BYTES, 0)))
ec_err_r(ENOMEM, EC_ENOMEM);
memcpy(c->signer_id, ec_cert_id(signer), EC_CERT_ID_BYTES);

//generate hash
unsigned char hash[EC_METHOD_BLAKE2B_512_BYTES];
rfail(ec_cert_hash(hash, c));

//sign
if(!(c->signature = talloc_size(c, crypto_sign_BYTES)))
if(!(c->signature = ec_record_buf(c, "_cert", "signature", crypto_sign_BYTES, EC_RECORD_NOSIGN)))
ec_err_r(ENOMEM, EC_ENOMEM);
crypto_sign_detached(c->signature, NULL, hash, sizeof(hash), signer->sk);

Expand Down
76 changes: 18 additions & 58 deletions src/libec/cert/export.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ static size_t ec_record_len(ec_record_t *r) {
* Get the required buffer length to export a certificate
*/
uint16_t ec_export_len(ec_cert_t *c, uint8_t export_flags) {
size_t length = sizeof(uint8_t) * 3 //layout version, cert flags, export flags
size_t length = sizeof(uint8_t) * 2 //layout version, cert flags
+ sizeof(uint16_t) //exported cert length
+ sizeof(uint32_t) * 2 //validity period
+ crypto_sign_PUBLICKEYBYTES //pk
+ (c->signer_id ? EC_CERT_ID_BYTES : 0) //signer id
+ (c->signature ? crypto_sign_BYTES : 0) //signature
+ ((c->sk && c->salt && (export_flags & EC_EXPORT_SECRET))
? crypto_sign_SECRETKEYBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES : 0) //sk & salt
+ sizeof(uint8_t); //NULL terminator
for(ec_record_t *r = c->records; r; r = r->next)
ec_record_t *sk = ec_record_match(ec_cert_records(c), "_cert", 0, "sk", NULL, 0);
ec_record_t *salt = ec_record_match(ec_cert_records(c), "_cert", 0, "salt", NULL, 0);
for(ec_record_t *r = c->records; r; r = r->next) {
if(!(export_flags & EC_EXPORT_SECRET) && (r == sk || r == salt))
continue;
length += ec_record_len(r); //records
}
return length <= EC_EXPORT_MAX ? length : 0;
}

Expand All @@ -56,35 +56,17 @@ size_t ec_export(unsigned char *dest, ec_cert_t *c, uint8_t export_flags) {
uint16_t length = ec_export_len(c, export_flags);
if(!length)
return 0;
if(!c->sk)
export_flags &= ~EC_EXPORT_SECRET;
if(c->signer_id)
export_flags |= EC_EXPORT_SIGNER;
if(c->signature)
export_flags |= EC_EXPORT_SIGNATURE;

*dest++ = c->version;
memcpy(dest, &length, sizeof(length)); dest += sizeof(length);
*dest++ = c->flags;
*dest++ = export_flags;
memcpy(dest, &c->valid_from, sizeof(c->valid_from)); dest += sizeof(c->valid_from);
memcpy(dest, &c->valid_until, sizeof(c->valid_until)); dest += sizeof(c->valid_until);
memcpy(dest, c->pk, crypto_sign_PUBLICKEYBYTES), dest += crypto_sign_PUBLICKEYBYTES;
if(c->signer_id) {
memcpy(dest, c->signer_id, EC_CERT_ID_BYTES);
dest += EC_CERT_ID_BYTES;
}
if(c->signature) {
memcpy(dest, c->signature, crypto_sign_BYTES);
dest += crypto_sign_BYTES;
}
if(c->sk && c->salt && (export_flags & EC_EXPORT_SECRET)) {
memcpy(dest, c->sk, crypto_sign_SECRETKEYBYTES);
dest += crypto_sign_SECRETKEYBYTES;
memcpy(dest, c->salt, crypto_pwhash_scryptsalsa208sha256_SALTBYTES);
dest += crypto_pwhash_scryptsalsa208sha256_SALTBYTES;
}
ec_record_t *sk = ec_record_match(ec_cert_records(c), "_cert", 0, "sk", NULL, 0);
ec_record_t *salt = ec_record_match(ec_cert_records(c), "_cert", 0, "salt", NULL, 0);
for(ec_record_t *r = c->records; r; r = r->next) {
if(!(export_flags & EC_EXPORT_SECRET) && (r == sk || r == salt))
continue;
uint16_t length = ec_record_len(r);
memcpy(dest, &length, sizeof(length)); dest += sizeof(length);
*dest++ = r->flags;
Expand Down Expand Up @@ -131,40 +113,11 @@ ec_cert_t *ec_import(unsigned char *src, size_t length, size_t *consumed) {

//flags & export flags
c->flags = *_bite(&src, &length, sizeof(c->flags));
uint8_t export_flags = *_bite(&src, &length, sizeof(export_flags));

//validity period
memcpy(&c->valid_from, _bite(&src, &length, sizeof(c->valid_from)), sizeof(c->valid_from));
memcpy(&c->valid_until, _bite(&src, &length, sizeof(c->valid_until)), sizeof(c->valid_until));

//pk
if(length < crypto_sign_PUBLICKEYBYTES ||
!(c->pk = talloc_memdup(c, _bite(&src, &length, crypto_sign_PUBLICKEYBYTES), crypto_sign_PUBLICKEYBYTES)))
return NULL;

//signer
if(export_flags & EC_EXPORT_SIGNER) {
if(length < EC_CERT_ID_BYTES ||
!(c->signer_id = talloc_memdup(c, _bite(&src, &length, EC_CERT_ID_BYTES), EC_CERT_ID_BYTES)))
return NULL;
}

//signature
if(export_flags & EC_EXPORT_SIGNATURE) {
if(length < crypto_sign_BYTES ||
!(c->signature = talloc_memdup(c, _bite(&src, &length, crypto_sign_BYTES), crypto_sign_BYTES)))
return NULL;
}

//sk
if(export_flags & EC_EXPORT_SECRET) {
if(length < crypto_sign_SECRETKEYBYTES + crypto_pwhash_scryptsalsa208sha256_SALTBYTES ||
!(c->sk = talloc_memdup(c, _bite(&src, &length, crypto_sign_SECRETKEYBYTES), crypto_sign_SECRETKEYBYTES)) ||
!(c->salt = talloc_memdup(c, _bite(&src, &length, crypto_pwhash_scryptsalsa208sha256_SALTBYTES),
crypto_pwhash_scryptsalsa208sha256_SALTBYTES)))
return NULL;
}

//records
for(ec_record_t **r = &c->records; length > EC_RECORD_MIN + sizeof(uint8_t); r = &(*r)->next) {
uint16_t record_length = 0;
Expand All @@ -187,6 +140,13 @@ ec_cert_t *ec_import(unsigned char *src, size_t length, size_t *consumed) {
}
}

//field buffers
c->pk = ec_record_buf(c, "_cert", "pk", crypto_sign_PUBLICKEYBYTES, EC_RECORD_SIGNED);
c->sk = ec_record_buf(c, "_cert", "sk", crypto_sign_SECRETKEYBYTES, 0);
c->salt = ec_record_buf(c, "_cert", "salt", crypto_pwhash_scryptsalsa208sha256_SALTBYTES, 0);
c->signer_id = ec_record_buf(c, "_cert", "signer_id", EC_CERT_ID_BYTES, EC_RECORD_SIGNED);
c->signature = ec_record_buf(c, "_cert", "signature", crypto_sign_BYTES, 0);

//NULL terminator && no remaining data && cert passes basic checks
if(*_bite(&src, &length, sizeof(uint8_t)) || length || ec_cert_check(NULL, c, EC_CHECK_CERT)) {
ec_cert_destroy(c);
Expand Down
2 changes: 1 addition & 1 deletion src/libec/include/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFT
#define EC_METHOD_BLAKE2B_512_BYTES 64

//layout version
#define EC_LAYOUT_VERSION 2
#define EC_LAYOUT_VERSION 3

//certificates
ec_err_t ec_cert_hash(unsigned char *hash, ec_cert_t *c);
Expand Down
19 changes: 4 additions & 15 deletions src/libec/include/ec.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,12 @@ char *ec_errstr(ec_err_t error);
#define EC_STRIP_SIGN (1 << 2) /*strip signer_id & signature*/

#define EC_EXPORT_SECRET (1 << 0) /*include secret key in exported cert*/
#define EC_EXPORT_SIGNER (1 << 1) /*include signer_id in exported cert*/
#define EC_EXPORT_SIGNATURE (1 << 2) /*include signature in exported cert*/

//limits
#define EC_EXPORT_HEADER (sizeof(uint8_t) * 3 /*version, flags & export flags*/ \
+ sizeof(uint16_t) /*cert length*/ + sizeof(uint32_t) * 2 /* validity period */ \
+ 32 * 2 /*pk & signer id*/ + 64 * 2 /*sk & signature*/)
#define EC_EXPORT_HEADER (sizeof(uint8_t) * 2 /*version & flags*/ \
+ sizeof(uint16_t) /*cert length*/ + sizeof(uint32_t) * 2 /* validity period */)
#define EC_EXPORT_OVERHEAD (EC_EXPORT_HEADER + 1 /* NULL terminator*/)
#define EC_EXPORT_MIN (sizeof(uint32_t) * 3 /*version, flags & export flags*/ \
+ sizeof(uint16_t) /*cert length*/ + sizeof(uint32_t) * 2 /* validity period */ \
+ 32 /*pk*/ + 1 /*NULL terminator*/)
#define EC_EXPORT_MIN EC_EXPORT_OVERHEAD
#define EC_EXPORT_MAX UINT16_MAX
#define EC_RECORD_MAX (UINT16_MAX - EC_EXPORT_OVERHEAD) /*max length of packed record*/
#define EC_RECORD_OVERHEAD (sizeof(uint16_t) /*record_len*/ + sizeof(uint8_t) /*key_len*/ + sizeof(uint8_t) /*flags*/)
Expand Down Expand Up @@ -303,7 +298,7 @@ ec_err_t ec_channel_decrypt(ec_channel_t *ch, unsigned char *buf, size_t len,
//get the remote cert
ec_cert_t *ec_channel_remote(ec_channel_t *ch);

/* +++++++++++++++ EXPORTED DATA LAYOUT V2 +++++++++++++++
/* +++++++++++++++ EXPORTED DATA LAYOUT V3 +++++++++++++++
Exported trust chain certificates are appended in order of ascending
authority (e.g. cert, signer, signer's signer etc.)
Expand All @@ -312,14 +307,8 @@ ec_cert_t *ec_channel_remote(ec_channel_t *ch);
1B (required) layout version
2B (required) certificate length
1B (required) certificate flags
1B (required) export flags
4B (required) valid_from
4B (required) valid_until
32B (required) ed25519 public key
32B (optional) ed25519 signer id
64B (optional) ed25519 signature (signed blake2b 512bit hash)
64B (optional) ed25519 secret key
32B (optional) salt for password-encryption of secret key
<records>
1B (required) NULL byte
Expand Down

0 comments on commit 032e418

Please sign in to comment.