1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "crypto/hmac.h"
6
7#include <nss.h>
8#include <pk11pub.h>
9
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "crypto/nss_util.h"
13#include "crypto/scoped_nss_types.h"
14
15namespace crypto {
16
17struct HMACPlatformData {
18  CK_MECHANISM_TYPE mechanism_;
19  ScopedPK11Slot slot_;
20  ScopedPK11SymKey sym_key_;
21};
22
23HMAC::HMAC(HashAlgorithm hash_alg)
24    : hash_alg_(hash_alg), plat_(new HMACPlatformData()) {
25  // Only SHA-1 and SHA-256 hash algorithms are supported.
26  switch (hash_alg_) {
27    case SHA1:
28      plat_->mechanism_ = CKM_SHA_1_HMAC;
29      break;
30    case SHA256:
31      plat_->mechanism_ = CKM_SHA256_HMAC;
32      break;
33    default:
34      NOTREACHED() << "Unsupported hash algorithm";
35      break;
36  }
37}
38
39HMAC::~HMAC() {
40}
41
42bool HMAC::Init(const unsigned char *key, int key_length) {
43  EnsureNSSInit();
44
45  if (plat_->slot_.get()) {
46    // Init must not be called more than twice on the same HMAC object.
47    NOTREACHED();
48    return false;
49  }
50
51  plat_->slot_.reset(PK11_GetBestSlot(plat_->mechanism_, NULL));
52  if (!plat_->slot_.get()) {
53    NOTREACHED();
54    return false;
55  }
56
57  SECItem key_item;
58  key_item.type = siBuffer;
59  key_item.data = const_cast<unsigned char*>(key);  // NSS API isn't const.
60  key_item.len = key_length;
61
62  plat_->sym_key_.reset(PK11_ImportSymKey(plat_->slot_.get(),
63                                          plat_->mechanism_,
64                                          PK11_OriginUnwrap,
65                                          CKA_SIGN,
66                                          &key_item,
67                                          NULL));
68  if (!plat_->sym_key_.get()) {
69    NOTREACHED();
70    return false;
71  }
72
73  return true;
74}
75
76bool HMAC::Sign(const std::string& data,
77                unsigned char* digest,
78                int digest_length) {
79  if (!plat_->sym_key_.get()) {
80    // Init has not been called before Sign.
81    NOTREACHED();
82    return false;
83  }
84
85  SECItem param = { siBuffer, NULL, 0 };
86  ScopedPK11Context context(PK11_CreateContextBySymKey(plat_->mechanism_,
87                                                       CKA_SIGN,
88                                                       plat_->sym_key_.get(),
89                                                       &param));
90  if (!context.get()) {
91    NOTREACHED();
92    return false;
93  }
94
95  if (PK11_DigestBegin(context.get()) != SECSuccess) {
96    NOTREACHED();
97    return false;
98  }
99
100  if (PK11_DigestOp(context.get(),
101                    reinterpret_cast<const unsigned char*>(data.data()),
102                    data.length()) != SECSuccess) {
103    NOTREACHED();
104    return false;
105  }
106
107  unsigned int len = 0;
108  if (PK11_DigestFinal(context.get(),
109                       digest, &len, digest_length) != SECSuccess) {
110    NOTREACHED();
111    return false;
112  }
113
114  return true;
115}
116
117}  // namespace crypto
118