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