Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

004-chromium js加解密接口源码流程分析 #85

Open
xinali opened this issue Apr 21, 2023 · 0 comments
Open

004-chromium js加解密接口源码流程分析 #85

xinali opened this issue Apr 21, 2023 · 0 comments

Comments

@xinali
Copy link
Owner

xinali commented Apr 21, 2023

chromium js加解密接口流程

文中所有涉及到的代码均来源于某个版本的开源chromium代码,无任何定制,部分内容由chatGPT生成

Author:xina1i && chatGPT

Update:2023/04/21

js代码样例

首先看一下chromium如何调用js加密接口,其中涉及异步调用,有两种方式。

第一种

async function encryptData(data) {
  const algorithm = {
    name: 'AES-CBC',
    iv: new Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54]), // 16-byte IV
    length: 128,
  };

  const keyData = new Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54]); // 16-byte AES key
  const key = await window.crypto.subtle.importKey('raw', keyData, algorithm, false, ['encrypt']);
  const encryptedData = await window.crypto.subtle.encrypt(algorithm, key, new TextEncoder().encode(data));
  const encryptedArray = new Uint8Array(encryptedData);
  let encryptedString = '';
  for (let i = 0; i < encryptedArray.length; i++) {
    encryptedString += String.fromCharCode(encryptedArray[i]);
  }
  return encryptedString;
}

// Usage
encryptData('hello world').then((result) => {
  console.log(result);
});

第二种

const algorithm = {
  name: 'AES-CBC',
  iv: new Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54]), // 16-byte IV
  length: 128,
};

const keyData = new Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54]); // 16-byte AES key
const keyPromise = window.crypto.subtle.importKey('raw', keyData, algorithm, false, ['encrypt']);
const encryptedPromise = keyPromise.then((key) => window.crypto.subtle.encrypt(algorithm, key, new TextEncoder().encode('hello world')));
encryptedPromise.then((encryptedData) => console.log(new Uint8Array(encryptedData).reduce((data, byte) => data + String.fromCharCode(byte), '')));

根据上面的代码首先来分析两个函数

window.crypto.subtle.importKey() 方法是 Web Crypto API 中的一个方法,用于导入一个已有的密钥,并将其封装为 CryptoKey 对象,以便进行加密、解密、签名和验证等操作。该方法接受四个参数:

  • format:一个字符串,指定要导入的密钥的格式。常见的格式有 "raw""pkcs8""spki""jwk"
  • keyData:要导入的密钥数据。根据不同的格式,keyData 可以是一个 ArrayBufferTypedArrayDataViewJsonWebKey 或类似的对象。
  • algorithm:一个对象,指定要使用的算法和参数。
  • extractable:一个布尔值,指定是否允许从导出的密钥中导出原始密钥材料。默认值为 false

使用 window.crypto.subtle.importKey() 方法,可以从不同的来源(如存储、网络等)导入现有的密钥,以便进行加密、解密等操作。导入的密钥可以是对称密钥或非对称密钥,具体取决于算法和数据格式。

例如,可以从一个 Uint8Array 对象中导入一个 AES 密钥,如下所示:

javascriptCopy code
const keyData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
const algorithm = {name: 'AES-CBC', length: 128};
const extractable = true;

const importedKey = await window.crypto.subtle.importKey(
    'raw', keyData, algorithm, extractable, ['encrypt', 'decrypt']);

在这个例子中,指定要导入一个 AES-CBC 算法的 128 位密钥,并且允许从导出的 CryptoKey 对象中导出原始密钥材料。然后将 Uint8Array 对象传递给 window.crypto.subtle.importKey() 方法,该方法将密钥数据封装为 CryptoKey 对象并返回。该对象可以用于加密、解密等操作。

chromium源码分析

chromium源码中, window.crypto.subtle.importKey() 方法的实现代码位于 third_party/blink/renderer/modules/crypto/subtle_crypto.cc 文件中。

具体来说,在 SubtleCrypto::importKey() 方法中,首先检查传入的参数是否合法,并根据传入的 format 字符串解析密钥数据。如果 format"jwk" ,则调用 JSONParseObject 方法解析传入的 JSON 对象。如果 format"raw" ,则将传入的密钥数据包装为一个 scoped_refptr<SecureBlob> 对象。如果 format"pkcs8""spki" ,则调用 crypto::ImportSubjectPublicKeyInfo()crypto::ImportPrivateKey() 方法解析密钥数据。

解析密钥数据后, SubtleCrypto::importKey() 方法根据传入的算法名称和参数,选择相应的密钥类型,并将密钥数据、算法和其他参数封装为一个 CryptoKey 对象,并将其返回给调用方。

这里有一个问题,就是js前端代码如何过渡到subtle_crypto.cc代码中,其实这里有个idl文件定义了js接口并规定了实现方式

third_party/blink/renderer/modules/crypto/subtle_crypto.idl

// https://w3c.github.io/webcrypto/Overview.html#subtlecrypto-interface

typedef DOMString KeyFormat;
typedef DOMString KeyUsage;
typedef (Dictionary or DOMString) AlgorithmIdentifier;

[
    Exposed=(Window,Worker)
] interface SubtleCrypto {
    [CallWith=ScriptState, MeasureAs=SubtleCryptoEncrypt] Promise<any> encrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoDecrypt] Promise<any> decrypt(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoSign] Promise<any> sign(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource data);
    [CallWith=ScriptState, ImplementedAs=verifySignature, MeasureAs=SubtleCryptoVerify] Promise<any> verify(AlgorithmIdentifier algorithm, CryptoKey key, BufferSource signature, BufferSource data);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoDigest] Promise<any> digest(AlgorithmIdentifier algorithm, BufferSource data);

    [CallWith=ScriptState, MeasureAs=SubtleCryptoGenerateKey] Promise<any> generateKey(AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoDeriveKey] Promise<any> deriveKey(AlgorithmIdentifier algorithm, CryptoKey baseKey, AlgorithmIdentifier derivedKeyType, boolean extractable, sequence<KeyUsage> keyUsages);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoDeriveBits] Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm, CryptoKey baseKey, unsigned long length);

    [CallWith=ScriptState, MeasureAs=SubtleCryptoImportKey] Promise<CryptoKey> importKey(KeyFormat format, (ArrayBuffer or ArrayBufferView or Dictionary) keyData, AlgorithmIdentifier algorithm, boolean extractable, sequence<KeyUsage> keyUsages);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoExportKey] Promise<any> exportKey(KeyFormat format, CryptoKey key);

    [CallWith=ScriptState, MeasureAs=SubtleCryptoWrapKey] Promise<any> wrapKey(KeyFormat format, CryptoKey key, CryptoKey wrappingKey, AlgorithmIdentifier wrapAlgorithm);
    [CallWith=ScriptState, MeasureAs=SubtleCryptoUnwrapKey] Promise<CryptoKey> unwrapKey(KeyFormat format, BufferSource wrappedKey, CryptoKey unwrappingKey, AlgorithmIdentifier unwrapAlgorithm, AlgorithmIdentifier unwrappedKeyAlgorithm, boolean extractable, sequence<KeyUsage> keyUsages);
}

根据这个文件chromium会在编译的过程中产生v8_subtle_crypto.h/v8_subtle_crypto.cc文件

v8_subtle_crypto.cc 文件是chromium 中用于实现 Web Crypto API 的前端代码之一,主要作用是实现 Web Crypto API 前端中一些与 V8 引擎交互的部分。具体来说, v8_subtle_crypto.cc 文件包含以下功能:

  1. 实现 V8 绑定: v8_subtle_crypto.cc 文件包含了与 V8 引擎相关的代码,用于将 Web Crypto API 的前端实现代码绑定到 V8 引擎中,从而可以在 JavaScript 中使用 Web Crypto API。在 v8_subtle_crypto.cc 文件中,使用 V8 的 FunctionTemplateObjectTemplate 类创建 JavaScript 对象和函数,并将它们绑定到 V8 引擎中。
  2. 处理 JavaScript 对象:在 JavaScript 中调用 Web Crypto API 方法时,会创建一些 JavaScript 对象和数组作为参数传递给底层的 C++ 实现。 v8_subtle_crypto.cc 文件中包含了将 JavaScript 对象和数组转换为 C++ 对象的代码,以及将 C++ 对象转换为 JavaScript 对象的代码。这些代码使用 V8LocalValue 类型来处理 JavaScript 对象和值。
  3. 实现 PromiseWeb Crypto API 中的一些方法返回的是 Promise 对象,用于表示异步操作的结果。 v8_subtle_crypto.cc 文件中包含了 Promise 的实现代码,用于将底层的异步操作转换为 JavaScript 中的 Promise。具体来说, v8_subtle_crypto.cc 文件中使用 V8Promise::Resolver 类创建 Promise 对象,并在异步操作完成后,调用 Promise::ResolverResolve() 方法将结果返回给 JavaScript

其中v8_subtle_crypto.h会引用subtle_crypto.h,所以它连接了C++源码的实现,这就定位到了window.subtle.crypto.importKey的C++实现,继续来看其importKey的具体实现(subtle_crypto.cc)

ScriptPromise SubtleCrypto::importKey(
    ScriptState* script_state,
    const String& raw_format,
    const ArrayBufferOrArrayBufferViewOrDictionary& raw_key_data,
    const AlgorithmIdentifier& raw_algorithm,
    bool extractable,
    const Vector<String>& raw_key_usages) {
  // Method described by:
  // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-importKey

  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
  ScriptPromise promise = result->Promise();
  
  // 解析传入的格式
  WebCryptoKeyFormat format;
  if (!CryptoKey::ParseFormat(raw_format, format, result))
    return promise;
  
  // 解析key用法
  WebCryptoKeyUsageMask key_usages;
  if (!CryptoKey::ParseUsageMask(raw_key_usages, key_usages, result))
    return promise;

  // In the case of JWK keyData will hold the UTF8-encoded JSON for the
  // JsonWebKey, otherwise it holds a copy of the BufferSource.
  WebVector<uint8_t> key_data;

  // 根据传入的格式,解析key,格式也就是上面列出来的format
  switch (format) {
    // 14.3.9.2: If format is equal to the string "raw", "pkcs8", or "spki":
    //
    //  (1) If the keyData parameter passed to the importKey method is a
    //      JsonWebKey dictionary, throw a TypeError.
    //
    //  (2) Let keyData be the result of getting a copy of the bytes held by
    //      the keyData parameter passed to the importKey method.
    case kWebCryptoKeyFormatRaw:
    case kWebCryptoKeyFormatPkcs8:
    case kWebCryptoKeyFormatSpki:
      if (raw_key_data.IsArrayBuffer()) {
        key_data = CopyBytes(raw_key_data.GetAsArrayBuffer());
      } else if (raw_key_data.IsArrayBufferView()) {
        key_data = CopyBytes(raw_key_data.GetAsArrayBufferView().View());
      } else {
        result->CompleteWithError(
            kWebCryptoErrorTypeType,
            "Key data must be a BufferSource for non-JWK formats");
        return promise;
      }
      break;
    // 14.3.9.2: If format is equal to the string "jwk":
    //
    //  (1) If the keyData parameter passed to the importKey method is not a
    //      JsonWebKey dictionary, throw a TypeError.
    //
    //  (2) Let keyData be the keyData parameter passed to the importKey
    //      method.
    case kWebCryptoKeyFormatJwk:
      if (raw_key_data.IsDictionary()) {
        // TODO(eroman): To match the spec error order, parsing of the
        // JsonWebKey should be done earlier (at the WebIDL layer of
        // parameter checking), regardless of the format being "jwk".
        if (!ParseJsonWebKey(raw_key_data.GetAsDictionary(), key_data, result))
          return promise;
      } else {
        result->CompleteWithError(kWebCryptoErrorTypeType,
                                  "Key data must be an object for JWK import");
        return promise;
      }
      break;
  }

  // 14.3.9.3: Let normalizedAlgorithm be the result of normalizing an
  //           algorithm, with alg set to algorithm and op set to
  //           "importKey".
  // 解析传入的算法
  WebCryptoAlgorithm normalized_algorithm;
  if (!ParseAlgorithm(raw_algorithm, kWebCryptoOperationImportKey,
                      normalized_algorithm, result))
    return promise;

  HistogramAlgorithm(ExecutionContext::From(script_state),
                     normalized_algorithm);
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      ExecutionContext::From(script_state)
          ->GetTaskRunner(blink::TaskType::kInternalWebCrypto);
  // 最终实现
  // WebCrypto Crypto
  Platform::Current()->Crypto()->ImportKey(
      format, std::move(key_data), normalized_algorithm, extractable,
      key_usages, result->Result(), std::move(task_runner));
  return promise;
}

分析完这部分代码可以发现,subtle_crypto.cc主要是解析传入的数据,但是最终的实现不在这里,经过分析,其最终的底层实现在**components/webcrypto** 目录下的代码中

components/webcrypto/webcrypto_impl.h代码中

class WebCryptoImpl : public blink::WebCrypto {
 public:
  WebCryptoImpl();

  ~WebCryptoImpl() override;

  void Encrypt(
      const blink::WebCryptoAlgorithm& algorithm,
      const blink::WebCryptoKey& key,
      blink::WebVector<unsigned char> data,
      blink::WebCryptoResult result,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
  void Decrypt(
      const blink::WebCryptoAlgorithm& algorithm,
      const blink::WebCryptoKey& key,
      blink::WebVector<unsigned char> data,
      blink::WebCryptoResult result,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
  void Digest(const blink::WebCryptoAlgorithm& algorithm,
              blink::WebVector<unsigned char> data,
              blink::WebCryptoResult result,
              scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
  void GenerateKey(
      const blink::WebCryptoAlgorithm& algorithm,
      bool extractable,
      blink::WebCryptoKeyUsageMask usages,
      blink::WebCryptoResult result,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
  void ImportKey(
      blink::WebCryptoKeyFormat format,
      blink::WebVector<unsigned char> key_data,
      const blink::WebCryptoAlgorithm& algorithm,
      bool extractable,
      blink::WebCryptoKeyUsageMask usages,
      blink::WebCryptoResult result,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
  // ...
}

以Encrypt为例分析

// * The methods named Do*() run on the crypto thread.
// * The methods named Do*Reply() run on the target Blink thread

// 加密后的数据处理,由blink线程来做
void DoEncryptReply(std::unique_ptr<EncryptState> state) {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
               "DoEncryptReply");
  CompleteWithBufferOrError(state->status, state->buffer, &state->result);
}

// crypto线程加密
void DoEncrypt(std::unique_ptr<EncryptState> passed_state) {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "DoEncrypt");
  EncryptState* state = passed_state.get();
  if (state->cancelled())
    return;
  // 这里会进入到algorithm_dispatch.cc中的Encrypt函数来做
  state->status =
      webcrypto::Encrypt(state->algorithm, state->key,
                         webcrypto::CryptoData(state->data), &state->buffer);
  state->origin_thread->PostTask(
      FROM_HERE, base::BindOnce(DoEncryptReply, std::move(passed_state)));
}

void WebCryptoImpl::Encrypt(
    const blink::WebCryptoAlgorithm& algorithm,
    const blink::WebCryptoKey& key,
    blink::WebVector<unsigned char> data,
    blink::WebCryptoResult result,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  DCHECK(!algorithm.IsNull());

  std::unique_ptr<EncryptState> state(new EncryptState(
      algorithm, key, std::move(data), result, std::move(task_runner)));

  // 在crypto线程上做DoEncrypt
  if (!CryptoThreadPool::PostTask(
          FROM_HERE, base::BindOnce(DoEncrypt, std::move(state)))) {
    CompleteWithThreadPoolError(&result);
  }
}

跟入**algorithm_dispatch.cc**

Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm,
                             const blink::WebCryptoKey& key,
                             const CryptoData& data,
                             std::vector<uint8_t>* buffer) {
  if (algorithm.Id() != key.Algorithm().Id())
    return Status::ErrorUnexpected();

  const AlgorithmImplementation* impl = nullptr;
  // 获取对应的算法进行加密操作
  Status status = GetAlgorithmImplementation(algorithm.Id(), &impl);
  if (status.IsError())
    return status;

  return impl->Encrypt(algorithm, key, data, buffer);
}

Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
               const blink::WebCryptoKey& key,
               const CryptoData& data,
               std::vector<uint8_t>* buffer) {
  if (!key.KeyUsageAllows(blink::kWebCryptoKeyUsageEncrypt))
    return Status::ErrorUnexpected();
  return EncryptDontCheckUsage(algorithm, key, data, buffer);

分析后可以发现algorigthm_registry.cc中会注册在algorithm_implementations.h 中声明的各种加解密算法,具体的算法components/webcrypto/algorithms

// algorigthm_registry.cc
class AlgorithmRegistry {
 public:
  // 创建各种算法实例
  AlgorithmRegistry()
      : sha_(CreateShaImplementation()),
        aes_gcm_(CreateAesGcmImplementation()),
        aes_cbc_(CreateAesCbcImplementation()),
        aes_ctr_(CreateAesCtrImplementation()),
        aes_kw_(CreateAesKwImplementation()),
        hmac_(CreateHmacImplementation()),
        rsa_ssa_(CreateRsaSsaImplementation()),
        rsa_oaep_(CreateRsaOaepImplementation()),
        rsa_pss_(CreateRsaPssImplementation()),
        ecdsa_(CreateEcdsaImplementation()),
        ecdh_(CreateEcdhImplementation()),
        hkdf_(CreateHkdfImplementation()),
        pbkdf2_(CreatePbkdf2Implementation()) {
    crypto::EnsureOpenSSLInit();
  }

  const AlgorithmImplementation* GetAlgorithm(
      blink::WebCryptoAlgorithmId id) const {
    switch (id) {
      case blink::kWebCryptoAlgorithmIdSha1:
      case blink::kWebCryptoAlgorithmIdSha256:
      case blink::kWebCryptoAlgorithmIdSha384:
      case blink::kWebCryptoAlgorithmIdSha512:
        return sha_.get();
      case blink::kWebCryptoAlgorithmIdAesGcm:
        return aes_gcm_.get();
      case blink::kWebCryptoAlgorithmIdAesCbc:
        return aes_cbc_.get();
      case blink::kWebCryptoAlgorithmIdAesCtr:
        return aes_ctr_.get();
      case blink::kWebCryptoAlgorithmIdAesKw:
        return aes_kw_.get();
      case blink::kWebCryptoAlgorithmIdHmac:
        return hmac_.get();
      case blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
        return rsa_ssa_.get();
      case blink::kWebCryptoAlgorithmIdRsaOaep:
        return rsa_oaep_.get();
      case blink::kWebCryptoAlgorithmIdRsaPss:
        return rsa_pss_.get();
      case blink::kWebCryptoAlgorithmIdEcdsa:
        return ecdsa_.get();
      case blink::kWebCryptoAlgorithmIdEcdh:
        return ecdh_.get();
      case blink::kWebCryptoAlgorithmIdHkdf:
        return hkdf_.get();
      case blink::kWebCryptoAlgorithmIdPbkdf2:
        return pbkdf2_.get();
      default:
        return nullptr;
    }
  }

 private:
  const std::unique_ptr<AlgorithmImplementation> sha_;
  const std::unique_ptr<AlgorithmImplementation> aes_gcm_;
  const std::unique_ptr<AlgorithmImplementation> aes_cbc_;
  const std::unique_ptr<AlgorithmImplementation> aes_ctr_;
  const std::unique_ptr<AlgorithmImplementation> aes_kw_;
  const std::unique_ptr<AlgorithmImplementation> hmac_;
  const std::unique_ptr<AlgorithmImplementation> rsa_ssa_;
  const std::unique_ptr<AlgorithmImplementation> rsa_oaep_;
  const std::unique_ptr<AlgorithmImplementation> rsa_pss_;
  const std::unique_ptr<AlgorithmImplementation> ecdsa_;
  const std::unique_ptr<AlgorithmImplementation> ecdh_;
  const std::unique_ptr<AlgorithmImplementation> hkdf_;
  const std::unique_ptr<AlgorithmImplementation> pbkdf2_;
};

}  // namespace

base::LazyInstance<AlgorithmRegistry>::Leaky g_algorithm_registry =
    LAZY_INSTANCE_INITIALIZER;

Status GetAlgorithmImplementation(blink::WebCryptoAlgorithmId id,
                                  const AlgorithmImplementation** impl) {
  // 根据id获取具体算法实现
  *impl = g_algorithm_registry.Get().GetAlgorithm(id);
  if (*impl)
    return Status::Success();
  return Status::ErrorUnsupported();
}

AES-CBC模式为例,其具体的加密实现在components/webcrypto/algorithms/aes_cbc.cc

Status AesCbcEncryptDecrypt(EncryptOrDecrypt cipher_operation,
                            const blink::WebCryptoAlgorithm& algorithm,
                            const blink::WebCryptoKey& key,
                            const CryptoData& data,
                            std::vector<uint8_t>* buffer) {
  crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);

  const blink::WebCryptoAesCbcParams* params = algorithm.AesCbcParams();
  const std::vector<uint8_t>& raw_key = GetSymmetricKeyData(key);

  if (params->Iv().size() != 16)
    return Status::ErrorIncorrectSizeAesCbcIv();

  // According to the openssl docs, the amount of data written may be as large
  // as (data_size + cipher_block_size - 1), constrained to a multiple of
  // cipher_block_size.
  base::CheckedNumeric<int> output_max_len = data.byte_length();
  output_max_len += AES_BLOCK_SIZE - 1;
  if (!output_max_len.IsValid())
    return Status::ErrorDataTooLarge();

  const unsigned remainder =
      base::ValueOrDieForType<unsigned>(output_max_len % AES_BLOCK_SIZE);
  if (remainder != 0)
    output_max_len += AES_BLOCK_SIZE - remainder;
  if (!output_max_len.IsValid())
    return Status::ErrorDataTooLarge();

  // Note: PKCS padding is enabled by default
  const EVP_CIPHER* const cipher = GetAESCipherByKeyLength(raw_key.size());
  DCHECK(cipher);

  bssl::ScopedEVP_CIPHER_CTX context;
  if (!EVP_CipherInit_ex(context.get(), cipher, nullptr, &raw_key[0],
                         params->Iv().Data(), cipher_operation)) {
    return Status::OperationError();
  }

  buffer->resize(base::ValueOrDieForType<size_t>(output_max_len));

  int output_len = 0;
  if (!EVP_CipherUpdate(context.get(), buffer->data(), &output_len,
                        data.bytes(), data.byte_length())) {
    return Status::OperationError();
  }
  int final_output_chunk_len = 0;
  if (!EVP_CipherFinal_ex(context.get(), buffer->data() + output_len,
                          &final_output_chunk_len)) {
    return Status::OperationError();
  }

  const unsigned int final_output_len =
      static_cast<unsigned int>(output_len) +
      static_cast<unsigned int>(final_output_chunk_len);

  buffer->resize(final_output_len);

  return Status::Success();
}

class AesCbcImplementation : public AesAlgorithm {
 public:
  AesCbcImplementation() : AesAlgorithm("CBC") {}

  Status Encrypt(const blink::WebCryptoAlgorithm& algorithm,
                 const blink::WebCryptoKey& key,
                 const CryptoData& data,
                 std::vector<uint8_t>* buffer) const override {
    return AesCbcEncryptDecrypt(ENCRYPT, algorithm, key, data, buffer);
  }

  Status Decrypt(const blink::WebCryptoAlgorithm& algorithm,
                 const blink::WebCryptoKey& key,
                 const CryptoData& data,
                 std::vector<uint8_t>* buffer) const override {
    return AesCbcEncryptDecrypt(DECRYPT, algorithm, key, data, buffer);
  }
};

其他说明

PostTask

chromium 中, PostTask 是一种用于在指定的线程上执行函数的机制。通常情况下, PostTask 用于在工作线程中执行任务,并将任务结果返回给主线程。

具体来说, PostTask 函数会将需要执行的函数包装成一个 Task 对象,并将该对象添加到指定线程的任务队列中。一旦该线程空闲,就会从任务队列中取出 Task 对象,并执行其中的函数。

chromium 中,可以通过 base::PostTask 函数和 base::PostTaskWithTraits 函数来使用 PostTask 机制。其中, PostTask 函数可以用于在指定线程上执行一般的函数,而 PostTaskWithTraits 函数可以用于在指定线程上执行具有特定属性(例如优先级、延迟等)的函数。

下面是一个示例,演示了如何使用 PostTask 函数在工作线程中执行任务并返回结果:

// 在工作线程中执行任务
void DoWork() {
  int result = ...;  // 执行任务并返回结果
  base::PostTask(FROM_HERE, base::BindOnce(&OnWorkDone, result));
}

// 在主线程中处理任务结果
void OnWorkDone(int result) {
  // 处理任务结果
}

在上面的代码中, DoWork 函数会在工作线程中执行任务,并将任务结果打包成一个 int 值。然后,它会通过 base::PostTask 函数将 OnWorkDone 函数打包成一个 Task 对象,并将该对象添加到主线程的任务队列中。一旦主线程空闲,就会从任务队列中取出 Task 对象,并执行其中的 OnWorkDone 函数,从而处理任务结果。

webcrypto/crypto区别

经过分析可以发现,components/webcryptothird_party/blink/renderer/modules/crypto 两个目录都包含与 Web Crypto API 相关的代码,但是它们的功能和作用有所不同。

components/webcrypto 目录包含了 chromium 浏览器中 Web Crypto API 的实现,包括算法实现、密钥管理和操作等。具体来说,该目录中包含了 algorithm_implementation.ccalgorithm_dispatch.cckey_material.ccoperation.cc 等文件,用于实现各种加密算法和操作。

third_party/blink/renderer/modules/crypto 目录则包含了 chromium 中用于实现 Web Crypto API 的前端代码,包括 JavaScript 和 Web IDL 文件等。该目录中包含了 subtle_crypto.ccsubtle_crypto.hsubtle_crypto_algorithms.ccsubtle_crypto_algorithms.h 等文件,用于将 JavaScript 的调用转换为底层的 C++ 实现。

总的来说,components/webcrypto 目录实现了 Web Crypto API 的底层功能,而 third_party/blink/renderer/modules/crypto 目录则实现了 Web Crypto API 的前端调用接口。这两个目录的代码工作协同,提供了完整的 Web Crypto API 实现。

在 chromium 79.0.3945.130 版本中,components/webcrypto/algorithm_dispatch.cccomponents/webcrypto/algorithm_implementation.cc 两个文件是用于实现 Web Crypto API 中各种算法的调度和实现。

algorithm_dispatch.cc 文件中包含一个名为 DispatchAlgorithm 的函数,用于根据传入的 Algorithm 对象调度合适的算法实现函数。该函数根据传入的算法名称和参数选择对应的算法实现函数,然后将控制权交给相应的实现函数进行处理。

algorithm_implementation.cc 文件中包含了各种算法的实现函数,例如 AES、RSA、HMAC 等。这些函数使用传入的密钥、参数和数据执行加密、解密、签名、验证等操作,并返回相应的结果。

这两个文件的作用是协同工作,使 Web Crypto API 能够支持多种加密算法和操作。在执行加密操作时,通过调用 DispatchAlgorithm 函数来选择合适的算法实现函数,然后由算法实现函数对数据进行处理,返回加密后的结果。在需要使用不同算法的情况下,只需要添加新的算法实现函数即可。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant