DidcommEncryptedMessage.fromPlaintext constructor
DidcommEncryptedMessage.fromPlaintext({
- KeyWrapAlgorithm keyWrapAlgorithm = KeyWrapAlgorithm.ecdh1PU,
- EncryptionAlgorithm encryptionAlgorithm = EncryptionAlgorithm.a256cbc,
- required Map<
String, dynamic> senderPrivateKeyJwk, - required List<
Map< recipientPublicKeyJwk,String, dynamic> > - required DidcommMessage plaintext,
Implementation
DidcommEncryptedMessage.fromPlaintext(
{KeyWrapAlgorithm keyWrapAlgorithm = KeyWrapAlgorithm.ecdh1PU,
EncryptionAlgorithm encryptionAlgorithm = EncryptionAlgorithm.a256cbc,
required Map<String, dynamic> senderPrivateKeyJwk,
required List<Map<String, dynamic>> recipientPublicKeyJwk,
required DidcommMessage plaintext}) {
if (keyWrapAlgorithm == KeyWrapAlgorithm.ecdh1PU &&
plaintext is DidcommPlaintextMessage &&
plaintext.from == null) {
throw Exception(
'For authcrypted messages the from-header of the plaintext message must not be null');
}
Map<String, dynamic> jweHeader = {};
jweHeader['enc'] = encryptionAlgorithm.value;
jweHeader['alg'] = keyWrapAlgorithm.value;
if (keyWrapAlgorithm == KeyWrapAlgorithm.ecdh1PU) {
jweHeader['apu'] = removePaddingFromBase64(
base64UrlEncode(utf8.encode(senderPrivateKeyJwk['kid'])));
}
jweHeader['skid'] = senderPrivateKeyJwk['kid'];
String curve = senderPrivateKeyJwk['crv']!;
String keyType = senderPrivateKeyJwk['kty']!;
List<String> receiverKeyIds = [];
for (Map<String, dynamic> key in recipientPublicKeyJwk) {
if (key['crv'] == curve) {
receiverKeyIds.add(key['kid']);
}
}
receiverKeyIds.sort();
String keyIdString = '';
for (var keyId in receiverKeyIds) {
keyIdString += '$keyId.';
}
if (keyIdString.isEmpty) {
throw Exception('Cant find keys with matching crv parameter');
}
keyIdString = keyIdString.substring(0, keyIdString.length - 1);
var apv = removePaddingFromBase64(
base64UrlEncode(sha256.convert(utf8.encode(keyIdString)).bytes));
jweHeader['apv'] = apv;
//1) Resolve dids to get public keys
//important: KeyAgreement section in diddoc
//apu = key-id of sender (first entry in keyAgreementArray) -> entry istsef (if did) or id of key-Object
//apv: get all key-ids in KeyAgreement _> search which match curve of sender key -> sort alphanumerical -> concat with . -> sha256 -> base64URL
//2) look for Key-Type and generate Ephermal Key
elliptic.Curve? c;
Object epkPrivate;
List<int> epkPublic = [];
if (curve.startsWith('P') || curve.startsWith('secp256k1')) {
if (curve == 'P-256') {
c = elliptic.getP256();
} else if (curve == 'P-384') {
c = elliptic.getP384();
} else if (curve == 'P-521') {
c = elliptic.getP521();
} else if (curve == 'secp256k1') {
c = elliptic.getSecp256k1();
} else {
throw UnimplementedError();
}
epkPrivate = c.generatePrivateKey();
} else if (curve.startsWith('X')) {
var eKeyPair = x25519.generateKeyPair();
epkPrivate = eKeyPair.privateKey;
epkPublic = eKeyPair.publicKey;
} else {
throw UnimplementedError();
}
Map<String, dynamic> epkJwk = {'kty': keyType, 'crv': curve};
if (epkPrivate is elliptic.PrivateKey) {
epkJwk['x'] = removePaddingFromBase64(
base64UrlEncode(intToBytes(epkPrivate.publicKey.X)));
epkJwk['y'] = removePaddingFromBase64(
base64UrlEncode(intToBytes(epkPrivate.publicKey.Y)));
} else if (epkPrivate is List<int>) {
epkJwk['x'] = removePaddingFromBase64(base64UrlEncode(epkPublic));
} else {
throw Exception('Unknown Key type');
}
jweHeader['epk'] = epkJwk;
//3) generate symmetric CEK
SymmetricKey cek;
if (encryptionAlgorithm == EncryptionAlgorithm.a256cbc) {
cek = SymmetricKey.generate(512);
} else {
cek = SymmetricKey.generate(256);
}
Encrypter e;
if (encryptionAlgorithm == EncryptionAlgorithm.a256cbc) {
e = cek.createEncrypter(algorithms.encryption.aes.cbcWithHmac.sha512);
} else if (encryptionAlgorithm == EncryptionAlgorithm.a256gcm) {
e = cek.createEncrypter(algorithms.encryption.aes.gcm);
} else {
throw UnimplementedError();
}
//4) Generate IV
//5) build aad ( ASCII(BASE64URL(UTF8(JWE Protected Header))) )
var aad = ascii.encode(removePaddingFromBase64(
base64UrlEncode(utf8.encode(jsonEncode(jweHeader)))));
//6) encrypt and get tag
var encrypted = e.encrypt(
Uint8List.fromList(utf8.encode(plaintext.toString())),
additionalAuthenticatedData: aad);
// 7) Encrypt cek for all recipients
List<Map<String, dynamic>> recipients = [];
for (var key in recipientPublicKeyJwk) {
if (key['crv'] == curve) {
Map<String, dynamic> r = {};
r['header'] = {'kid': key['kid']};
var encryptedCek = _encryptSymmetricKey(
cek, keyWrapAlgorithm.value, curve, key, epkPrivate, apv,
c: c,
senderPrivateKeyJwk: senderPrivateKeyJwk,
tag: encrypted.authenticationTag);
r['encrypted_key'] =
removePaddingFromBase64(base64UrlEncode(encryptedCek.data));
recipients.add(r);
}
}
//9) put everything together
protectedHeader = ascii.decode(aad);
tag =
removePaddingFromBase64(base64UrlEncode(encrypted.authenticationTag!));
iv = removePaddingFromBase64(
base64UrlEncode(encrypted.initializationVector!));
ciphertext = removePaddingFromBase64(base64UrlEncode(encrypted.data));
this.recipients = recipients;
}