Last active
August 29, 2015 14:16
-
-
Save landonf/bd09de4f4322bb7b699b to your computer and use it in GitHub Desktop.
declarative vs imperative codecs -- usage example of our C++ re-implementation of the Scala scodec library.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Decoding and encoding of the entire file format | |
/** | |
* @internal | |
* | |
* Create a Codec instance for VDEFileItem structures. | |
*/ | |
static Codec<VDEFileItem> fileItemCodec() { | |
using namespace codecs; | |
const auto fileMagic = ByteVector::Bytes("VPVDE", 5, false); | |
const auto varLengthBytes = variableSizeBytes(uint32L, identityBytes); | |
const auto vdeRecordVersion = ( | |
("compat_version" | codecs::uint8 ) & | |
("feature_version" | codecs::uint8 ) | |
).as<VDERecordVersion>(); | |
const auto vdeSection = ( | |
("offset" | uint64L ) & | |
("length" | uint64L ) | |
).as<VDESectionRecord>(); | |
const auto vdeHeader = ( | |
("magic" | constant(fileMagic) ) >> | |
("file_version" | vdeRecordVersion ) & | |
("vde_encrypted_sect" | vdeSection ) & | |
("vde_session_sect" | vdeSection ) | |
).as<VDEFileHeader>(); | |
const auto pbkdf2Params = ( | |
("iters" | uint32L ) & | |
("salt" | varLengthBytes ) | |
).as<VDESessionMasterKeyParams>(); | |
// TODO: Until we generalize Codec::as(), we have to lift the single Codec to a TupledCodec here | |
const auto hkdfParams = TupledCodec<ByteVector>( | |
("salt" | varLengthBytes ) | |
).as<VDESessionSubkeyParams>(); | |
const auto vdeSession = ( | |
("version" | vdeRecordVersion ) & | |
("pbkdf2_params" | pbkdf2Params ) & | |
("hkdf_params" | hkdfParams ) & | |
("dpk" | varLengthBytes ) | |
).as<VDESession>(); | |
return ( | |
("vde_header" | vdeHeader ) >>= [vdeSession](const VDEFileHeader &hdr) { | |
// TODO - offset combinators | |
auto padding1Length = hdr.encryptedSection().offset() - VDE_FILE_HEADER_SIZE; | |
auto encryptedLength = hdr.encryptedSection().length(); | |
auto padding2Length = hdr.sessionSection().offset() - (hdr.encryptedSection().offset() + encryptedLength); | |
auto sessionLength = hdr.sessionSection().length(); | |
return ( | |
("padding_1" | ignore(padding1Length) ) >> | |
("encrypted_data" | bytes(encryptedLength) ) & | |
("padding_2" | ignore(padding2Length) ) >> | |
("vde_session" | fixedSizeBytes(sessionLength, vdeSession) ) | |
); | |
} | |
).as<VDEFileItem>(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Decoding (but not encoding) of only the file header | |
/** | |
* Read a VDE header at @a position, returning the number of bytes read. | |
* | |
* @param position The offset within @a data from which the array will be read. | |
* @param bytesRead If non-NULL, will be set to the total number of bytes consumed at @a position, including the size of any | |
* header data. | |
* @param outError If non-NULL and an error occurs, @a outError will be populated with an NSError instance in the PLCryptoErrorDomain. | |
* | |
* @return The VDE header, or nil if if an error occurs. | |
* | |
* @par Error Codes | |
* This method may fail in the following cases: | |
* | |
* - If less than the required number of bytes are available, PLCryptoInsufficientData will be returned. | |
* - If the decoded header's magic value does not match the expected VDE header magic, PLCryptoInvalidData will be returned. | |
*/ | |
- (VDEHeader *) decodeHeaderAt: (size_t) position bytesRead: (size_t *) bytesRead error: (NSError **) outError { | |
/* Fetch the header bytes */ | |
vde_header_t header; | |
if (![self getBytes: &header position: position length: sizeof(header) error: outError]) | |
return nil; | |
if (bytesRead != NULL) | |
*bytesRead = sizeof(header); | |
/* Verify the magic value */ | |
if (memcmp(header.magic, vde_magic, sizeof(vde_magic)) != 0) { | |
vde_populate_error(outError, VDEInvalidData, NSLocalizedString(@"The provided data does not appear to be a valid VDE item header", nil), nil, nil); | |
return nil; | |
} | |
/* Assemble the result */ | |
VDERecordVersion *version = [[[VDERecordVersion alloc] initWithCompatibilityVersion: header.version.compat | |
featureVersion: header.version.feature] autorelease]; | |
VDESectionRecord *encryptedSection = [[[VDESectionRecord alloc] initWithOffset: OSSwapLittleToHostInt64(header.encrypted_section.offset) | |
length: OSSwapLittleToHostInt64(header.encrypted_section.length)] autorelease]; | |
VDESectionRecord *session = [[[VDESectionRecord alloc] initWithOffset: OSSwapLittleToHostInt64(header.session_section.offset) | |
length: OSSwapLittleToHostInt64(header.session_section.length)] autorelease]; | |
return [[[VDEHeader alloc] initWithVersion: version | |
encryptedSection: encryptedSection | |
sessionSection: session] autorelease];; | |
} | |
/** | |
* Read a VDE length-prefixed byte array at @a position, returning the number of bytes read. | |
* | |
* @param position The offset within @a data from which the array will be read. | |
* @param bytesRead If non-NULL, will be set to the total number of bytes consumed at @a position, including the size of any | |
* header data. | |
* @param outError If non-NULL and an error occurs, @a outError will be populated with an NSError instance in the PLCryptoErrorDomain. | |
* | |
* @return The VDE byte array's data, or nil if if an error occurs. | |
* | |
* @par Error Codes | |
* This method may fail with a PLCryptoInsufficientData error. | |
*/ | |
- (NSData *) decodeByteArrayAt: (size_t) position bytesRead: (size_t *) bytesRead error: (NSError **) outError { | |
size_t initialPosition = position; | |
/* Fetch the byte array */ | |
vde_bytearray_t barray; | |
if (![self getBytes: &barray position: position length: sizeof(barray) error: outError]) | |
return nil; | |
barray.length = OSSwapLittleToHostInt32(barray.length); | |
position += sizeof(vde_bytearray_t); | |
/* Fetch the actual bytes */ | |
NSMutableData *result = [NSMutableData dataWithLength: barray.length]; | |
if (![self getBytes: result.mutableBytes position: position length: barray.length error: outError]) | |
return nil; | |
position += barray.length; | |
/* Provide the total number of bytes read */ | |
if (bytesRead != NULL) | |
*bytesRead = position - initialPosition; | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment