github.com/zntrio/harp/v2@v2.0.9/pkg/container/seal/v2/key.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package v2 19 20 import ( 21 "bytes" 22 "crypto/ecdsa" 23 "crypto/elliptic" 24 "crypto/hmac" 25 "crypto/rand" 26 "crypto/sha512" 27 "encoding/base64" 28 "fmt" 29 "strings" 30 31 "github.com/awnumar/memguard" 32 33 "github.com/zntrio/harp/v2/pkg/container/seal" 34 "github.com/zntrio/harp/v2/pkg/sdk/security/crypto/deterministicecdsa" 35 36 "golang.org/x/crypto/pbkdf2" 37 ) 38 39 const ( 40 PublicKeyPrefix = "v2.sk." 41 PrivateKeyPrefix = "v2.ck." 42 ) 43 44 // CenerateKey create an ECDSA P-384 key pair used as container identifier. 45 func (a *adapter) GenerateKey(fopts ...seal.GenerateOption) (publicKey, privateKey string, err error) { 46 // Prepare defaults 47 opts := &seal.GenerateOptions{ 48 DCKDMasterKey: nil, 49 DCKDTarget: "", 50 RandomSource: rand.Reader, 51 } 52 53 // Apply optional parameters 54 for _, f := range fopts { 55 f(opts) 56 } 57 58 // Master key derivation 59 if opts.DCKDMasterKey != nil { 60 // PBKDF2-SHA512(masterKey, HMAC-SHA-512('harp deterministic salt v2', Target), 250000, 64) 61 // Don't clean bytes, already done by memguard. 62 masterKey := opts.DCKDMasterKey.Bytes() 63 if len(masterKey) < 32 { 64 return "", "", fmt.Errorf("the master key must be 32 bytes long at least") 65 } 66 67 // Generate deterministic salt 68 h := hmac.New(sha512.New, []byte("harp deterministic salt v2")) 69 h.Write([]byte(opts.DCKDTarget)) 70 salt := h.Sum(nil) 71 defer memguard.WipeBytes(salt) 72 73 // Derive deterministic container key using PBKDF2-SHA512 74 dk := pbkdf2.Key(masterKey[:32], salt, 250000, 64, sha512.New) 75 defer memguard.WipeBytes(dk) 76 77 // Assign to seed 78 opts.RandomSource = bytes.NewBuffer(dk) 79 } 80 81 // Generate ECDSA P-384 container key pair 82 priv, errGen := deterministicecdsa.GenerateKey(elliptic.P384(), opts.RandomSource) 83 if errGen != nil { 84 return "", "", fmt.Errorf("unable to generate container key: %w", errGen) 85 } 86 87 // Encode keys 88 encodedPub := append([]byte(PublicKeyPrefix), base64.RawURLEncoding.EncodeToString(elliptic.MarshalCompressed(priv.Curve, priv.PublicKey.X, priv.PublicKey.Y))...) 89 encodedPriv := append([]byte(PrivateKeyPrefix), base64.RawURLEncoding.EncodeToString(priv.D.Bytes())...) 90 91 // No error 92 return string(encodedPub), string(encodedPriv), nil 93 } 94 95 // PublicKeys return the appropriate key format used by the sealing strategy. 96 func (a *adapter) publicKeys(keys ...string) ([]*ecdsa.PublicKey, error) { 97 // v2.pk.[data] 98 res := []*ecdsa.PublicKey{} 99 100 for _, key := range keys { 101 // Check key prefix 102 if !strings.HasPrefix(key, PublicKeyPrefix) { 103 return nil, fmt.Errorf("unsuppored public key %q for v2 seal algorithm", key) 104 } 105 106 // Remove prefix if exists 107 key = strings.TrimPrefix(key, PublicKeyPrefix) 108 109 // Decode key 110 keyRaw, err := base64.RawURLEncoding.DecodeString(key) 111 if err != nil { 112 return nil, fmt.Errorf("unable to decode public key %q: %w", key, err) 113 } 114 115 // Public key sanity checks 116 if len(keyRaw) != publicKeySize { 117 return nil, fmt.Errorf("invalid public key length for key %q", key) 118 } 119 120 // Decode the compressed point 121 x, y := elliptic.UnmarshalCompressed(elliptic.P384(), keyRaw) 122 if x == nil { 123 return nil, fmt.Errorf("invalid public key %q", key) 124 } 125 126 // Reassemble the public key 127 pub := ecdsa.PublicKey{ 128 Curve: elliptic.P384(), 129 X: x, 130 Y: y, 131 } 132 133 // Append it to sealing keys 134 res = append(res, &pub) 135 } 136 137 // No error 138 return res, nil 139 }