Skip to content

Latest commit

 

History

History
406 lines (303 loc) · 11.5 KB

File metadata and controls

406 lines (303 loc) · 11.5 KB
final unwrappedKey = secretKey.unwrap(
  wrappedKeyInBytes,
);
final hmacKey = HmacSecretKey.importUnwrappedKey(unwrappedKey, Hash.sha256);





final hmacKey = await HmacSecretKey.generate(Hash.sha256);

final wrappedKeyAsBytes = secretKey.wrapKey(hmacKey, iv);

final hmacKey = await secretKey.unwrap(
  wrappedKeyAsBytes,
  HmacSecretKey.options(Hash.sha256),
  iv,
)




final hmacKey = hkdfKey.deriveKey(
  HmacSecretKey.options(Hash.sha256),
  salt,
  info,
)

// ---------------------------------------


final hmacKey = await HmacSecretKey.generate(Hash.sha256);

final wrappedKeyAsBytes = secretKey.wrapKey(hmacKey, iv);

final hmacKey = await HmacSecretKey.importWrappedKey(
  secretKey.unwrapKey(wrappedKeyAsBytes, iv),
  Hash.sha256,
);

final hmacKey = await HmacSecretKey.importDerivedKey(
  hkdfKey.deriveKey(salt, info),
  Hash.sha256,
);


// ---------------------------------------


final hmacKey = await HmacSecretKey.generate(Hash.sha256);

final wrappedKeyAsBytes = secretKey.wrapKey(hmacKey, iv);

final hmacKey = await HmacSecretKey.importOpaqueKey(
  secretKey.unwrapKey(wrappedKeyAsBytes, iv), // returns OpaqueKey
  Hash.sha256,
);

final hmacKey = await HmacSecretKey.importOpaqueKey(
  hkdfKey.deriveKey(salt, info), // returns OpaqueKey
  Hash.sha256,
);


@sealed
abstract class OpaqueKey {}

// ---------------------------------------


final hmacKey = await HmacSecretKey.generate(Hash.sha256);

final wrappedKeyAsBytes = await secretKey.wrapJsonWebKey(hmacKey, iv);

final hmacKey = await secretKey
    .unwrapJsonWebKey(wrappedKeyAsBytes, iv)
    .asHmacSecretKey(Hash.sha256);

final hmacKey = await hkdfKey
  .deriveKey(salt, info)
  .asHmacSecretKey(Hash.sha256);

@sealed
abstract class OpaqueKey {
  Future<HmacSecretKey> asHmacSecretKey(Hash hash);
}

// ---------------------------------------

// Note: I do not like the naming, it allows for the right typing, but these
//       are not good names, they will not be located next to each other in
//       generated documentation or in code completion.
abstract class JsonWebExportableKey {
  Future<Map<String, dynamic>> exportJsonWebKey();
}
abstract class RawExportableKey {
  Future<Uint8List> exportRawKey();
}
abstract class SpkiExportableKey {
  Future<Uint8List> exportSpkiKey();
}
abstract class Pkcs8ExportableKey {
  Future<Uint8List> exportPkcs8Key();
}

final hmacKey = await HmacSecretKey.generate(Hash.sha256);

// It's weird that wrap<format>Key has the format in the method name.
// but unwrapKey doesn't have format in the name.
final = wrappedKeyAsBytes await secretKey.wrapJsonWebKey(
  hmacKey, // this is a JsonWebExportableKey
  iv,
);

final = wrappedKeyAsBytes await secretKey.wrapRawKey(
  hmacKey, // this is a JsonWebExportableKey
  iv,
);


final hmacKey = await secretKey.unwrapKey(
  // This must be a ImportKeyOptions<T>, and then unwrapKey returns Future<T>
  // This allows us to ensure:
  //   - Typing when specifying HMAC import parameters
  //   - Prevents the user from trying using SPKI with options belonging to
  //     a private RsaPssPrivateKey which doesn't support importSpkiKey(...)
  //
  // Note: I do not like the naming
  HmacSecretKey.importJsonWebKeyOptions(Hash.sha256),
  wrappedKeyAsBytes,
  iv,
);

final hmacKey = await secretKey.unwrapKey(
  HmacSecretKey.importRawKeyOptions(Hash.sha256),
  wrappedKeyAsBytes,
  iv,
);

final hmacKey = await hkdfKey.deriveKey(
  // This must be a DeriveKeyOptions<T>, and then deriveKey returns Future<T>
  // This allows us to limit what keys can be derived, webcrypto can only derive
  // HMAC, AES-CBC, AES-CTR, AES-GCM and AES-KW. 
  HmacSecretKey.deriveKeyOptions(Hash.sha256),
  salt,
  info,
);

@sealed
abstract class ImportKeyOptions<T> {}

@sealed
abstract class DeriveKeyOptions<T> {}

// We could do: ImportJsonWebKeyOptions, as illustrated below.
// Then HmacSecretKey.importJsonWebKeyOptions would return a
// ImportJsonWebKeyOptions<HmacSecretKey>
//
// So users would be able to do:
// final hmacKey = await HmacSecretKey.importJsonWebKeyOptions(Hash.sha256).import(
//  json.decode(jwt),
// );
//
// So one could even argue that it would make unnecessary to have:
// final hmacKey = await HmacSecretKey.importJsonWebKey(
//   json.decode(jwt),
//   Hash.sha256,
// );
// I'm not sure this is a good idea. Because it promotes doing things like:
//   final opts = HmacSecretKey.importJsonWebKeyOptions(Hash.sha256);
// And then later doing:
//   ... = opts.import(...);
// And I'm not sure we should provide that level of abstractions. It might be
// somewhat risky to separate these things.
// Also some people might get the idea that an ImportJsonWebKeyOptions is
// something that can be implemented -- which it can't.
// Indeed: ImportKeyOptions having no members makes it clear that this is an
//         opaque object, not an interface that can be satisfied by the user.
abstract class ImportJsonWebKeyOptions<T> implements ImportKeyOptions<T> {
  Future<T> import(Map<String, dynamic> jwk);
}


// ---------------------------------------

final hmacKey = await HmacSecretKey.generate(Hash.sha256);


final = wrappedKeyAsBytes await secretKey.wrapKey(
  // This is a WrapKeyInput
  // each xxxKey type can implement: wrappable<format>Key() -> WrapKeyInput
  // Typing ensures you won't try to wrap an HMAC key as SPKI, because
  // you simply cannot express it.
  hmacKey.asRawWrapKeyInput(), // better name please!
  // These are the same options as secretKey.encryptBytes, which differs
  // depending on the type of [secretKey] -- which is typed! :D
  iv,
);


final hmacKey = await secretKey.unwrapKey(
  // This must be a ImportKeyOptions<T>, and then unwrapKey returns Future<T>
  // This allows us to ensure:
  //   - Typing when specifying HMAC import parameters
  //   - Prevents the user from trying using SPKI with options belonging to
  //     a private RsaPssPrivateKey which doesn't support importSpkiKey(...)
  //
  // Note: I do not like the naming, I'd love something shorter, maybe just:
  //       HmacSecretKey.importJwkOptions or HmacSecretKey.importJwkParams
  HmacSecretKey.importJsonWebKeyOptions(Hash.sha256),
  wrappedKeyAsBytes,
  iv,
);

final hmacKey = await secretKey.unwrapKey(
  HmacSecretKey.importRawKeyOptions(Hash.sha256),
  wrappedKeyAsBytes,
  iv,
);

final hmacKey = await hkdfKey.deriveKey(
  // This must be a DeriveKeyOptions<T>, and then deriveKey returns Future<T>
  // This allows us to limit what keys can be derived, webcrypto can only derive
  // HMAC, AES-CBC, AES-CTR, AES-GCM and AES-KW.
  //
  // This is almost the same as ImportKeyOptions, except not all algorithms
  // support being derived, only things that can be imported from raw.
  // And even then, not all key types that can be imported from raw!
  HmacSecretKey.deriveKeyOptions(Hash.sha256),
  salt,
  info,
);

@sealed
abstract class WrapKeyInput {} // Could also be called WrappableKey

@sealed
abstract class ImportKeyOptions<T> {} // Could also be called WrappedKeyOptions

@sealed
abstract class DeriveKeyOptions<T> {}


// ---------------------------------------

// Same proposal as above, except we get rid of the <T> type parameterization!

// WARNING: This proposal has the problem that the VERB we're doing
//          such as wrap / unwrap / derive just returns an <..>Options object
//          instead of returning a Future and actually doing the work.
//          This might give the wrong impression of what object is doing the work.

final hmacKey = await HmacSecretKey.generate(Hash.sha256);

final wrappedKeyAsBytes await = hmacKey.exportWrappedRawKey(
  // WrapKeyOptions
  secretKey.wrapKeyOptions(iv),
);

final hmacKey = await HmacSecretKey.importWrappedJsonWebKey(
  // UnwrapKeyOptions
  secretKey.unwrapKeyOptions(iv),
  wrappedKeyAsBytes,
  Hash.sha256,
);

final hmacKey = await HmacSecretKey.importWrappedRawKey(
  secretKey.unwrapKeyOptions(iv),
  wrappedKeyAsBytes,
  Hash.sha256,
);

final hmacKey = await HmacSecretKey.importDerivedKey(
  // DeriveKeyOptions
  hkdfKey.deriveKeyOptions(salt, info),
);

// ERROR: The opaque objects are abstractions that invite the user to reuse
//        the object for unwrapping, wrapper or deriving many keys.
//        The user may get the idea that they creatae a single WrapKeyOptions
//        object, and use it to wrap a lot of keys.
//        THIS is EXTREMELY bad, because WrapKeyOptions holds iv and other things
//        that should be unique and unprediable for each operation.
//        Depending on the primitives, but reuse is generally unsafe.
//        So abstractions that creates an illusion that reuse is okay are BAD!
@sealed
abstract class WrapKeyOptions {} // Could also be called KeyWrapper

@sealed
abstract class UnwrapKeyOptions {} // Could also be called KeyUnwrapper

@sealed
abstract class DeriveKeyOptions {} // Could alos be called KeyDeriver



// ---------------------------------------

// I fear users will able to read documentation very backwards to get this working.
// It might type nicely, and it might look good in a draft..
final hmacKey = await HmacSecretKey.options(Hash.sha256).generate();
final hmacKey = await HmacSecretKey.options(Hash.sha256).raw.import(...);
final hmacKey = await HmacSecretKey.options(Hash.sha256).jwk.import(...);

final wrappedKeyAsBytes = await secretKey.wrapJsonWebKey(hmacKey, iv);

final hmacKey = await secretKey.unwrapKey(
  // KeyImportOptions
  HmacSecretKey.options(Hash.sha256).jwk,
  wrappedKeyAsBytes,
  iv,
);

final hmacKey = await hkdfKey.deriveKey(
  // Only allows KeyImportRawOrDeriveOptions, which only keys that support
  // being derived can create.
  HmacSecretKey.options(Hash.sha256).raw,
  salt,
  info,
);

// ERROR: THis makes NO SENSE... Sorry!!!

@sealed
abstract class KeyOptions<T> {}

@sealed
abstract class KeyImportOptions<T> {}

abstract class KeyImportRawOptions<T> implements KeyImportOptions<T> {
  Future<T> import(List<int> keyData);
}

abstract class KeyImportRawOrDeriveOptions<T> implements KeyImportOptions<T> {
  Future<T> import(List<int> keyData);
}

abstract class KeyImportJwkOptions<T> implements KeyImportOptions<T> {
  Future<T> import(Map<String, dynamic> jwk);
}



// ---------------------------------------

final hmacKey = await HmacSecretKey.generate(Hash.sha256);


final wrappedKeyAsBytes = await secretKey.wrapKey(
  // this is a WrapKeyOptions -- just a way type key + format, ensuring you
  // can't use a format not supported for a given key.
  // Also weird to have a format enum, not used in import/export methods!
  hmacKey.wrapRawKeyOptions(),
  // options for encryptBytes, differs depending on secretKey type!
  iv,
);

final wrappedKeyAsBytes = await secretKey.wrapKey(
  hmacKey.wrapJsonWebKeyOptions(),
  iv,
);


final hmacKey = await secretKey.unwrapKey(
  // This is a UnwrapKeyOptions -- which is format + import options
  HmacSecretKey.unwrapJsonWebKeyOptions(Hash.sha256),
  wrappedKeyAsBytes,
  // options for decryptBytes, differs depending on secretKey type
  iv,
);

final hmacKey = await secretKey.unwrapKey(
  HmacSecretKey.unwrapRawKeyOptions(Hash.sha256),
  wrappedKeyAsBytes,
  iv,
);


final hmacKey = await hkdfKey.deriveKey(
  // This must be a DeriveKeyOptions<T>, and then deriveKey returns Future<T>
  // This allows us to limit what keys can be derived, webcrypto can only derive
  // HMAC, AES-CBC, AES-CTR, AES-GCM and AES-KW.
  HmacSecretKey.deriveKeyOptions(Hash.sha256),
  salt,
  info,
);

@sealed
abstract class WrapKeyOptions {}

@sealed
abstract class UnwrapKeyOptions<T> {}

@sealed
abstract class DeriveKeyOptions<T> {}

Note: if you need to do "key_ops" and "use" validation (which you might need to) then look at the chrome implementation here: https://source.chromium.org/chromium/chromium/src/+/main:components/webcrypto/j