github.com/zntrio/harp/v2@v2.0.9/pkg/container/seal/v1/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 v1 19 20 import ( 21 "bytes" 22 "crypto/rand" 23 "encoding/base64" 24 "fmt" 25 "strings" 26 27 "github.com/awnumar/memguard" 28 29 "github.com/zntrio/harp/v2/pkg/container/seal" 30 "github.com/zntrio/harp/v2/pkg/sdk/security/crypto/extra25519" 31 32 "golang.org/x/crypto/argon2" 33 "golang.org/x/crypto/blake2b" 34 "golang.org/x/crypto/nacl/box" 35 ) 36 37 const ( 38 PublicKeyPrefix = "v1.sk." 39 PrivateKeyPrefix = "v1.ck." 40 ) 41 42 // ----------------------------------------------------------------------------- 43 44 // CenerateKey create an X25519 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 // Argon2ID(masterKey, Blake2B-512('harp deterministic salt v1', Target), 1, 64Mb, 4, 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, err := blake2b.New512([]byte("harp deterministic salt v1")) 69 if err != nil { 70 return "", "", fmt.Errorf("unable to initialize salt derivation: %w", err) 71 } 72 h.Write([]byte(opts.DCKDTarget)) 73 salt := h.Sum(nil) 74 defer memguard.WipeBytes(salt) 75 76 // Derive deterministic container key using Argon2id 77 dk := argon2.IDKey(masterKey[:32], salt, 1, 64*1024, 4, 64) 78 defer memguard.WipeBytes(dk) 79 80 // Assign to seed 81 opts.RandomSource = bytes.NewBuffer(dk) 82 } 83 84 // Generate x25519 container key pair 85 pub, priv, errGen := box.GenerateKey(opts.RandomSource) 86 if errGen != nil { 87 return "", "", fmt.Errorf("unable to generate container key: %w", errGen) 88 } 89 90 // Encode keys 91 encodedPub := append([]byte(PublicKeyPrefix), base64.RawURLEncoding.EncodeToString(pub[:])...) 92 encodedPriv := append([]byte(PrivateKeyPrefix), base64.RawURLEncoding.EncodeToString(priv[:])...) 93 94 // No error 95 return string(encodedPub), string(encodedPriv), nil 96 } 97 98 // PublicKeys return the appropriate key format used by the sealing strategy. 99 func (a *adapter) publicKeys(keys ...string) ([]*[32]byte, error) { 100 // v1.pk.[data] 101 res := []*[publicKeySize]byte{} 102 103 for _, key := range keys { 104 // Check key prefix 105 if !strings.HasPrefix(key, PublicKeyPrefix) { 106 return nil, fmt.Errorf("unsuppored public key %q for v1 seal algorithm", key) 107 } 108 109 // Remove prefix if exists 110 key = strings.TrimPrefix(key, PublicKeyPrefix) 111 112 // Decode key 113 keyRaw, err := base64.RawURLEncoding.DecodeString(key) 114 if err != nil { 115 return nil, fmt.Errorf("unable to decode public key %q: %w", key, err) 116 } 117 118 // Public key sanity checks 119 if len(keyRaw) != publicKeySize { 120 return nil, fmt.Errorf("invalid public key length for key %q", key) 121 } 122 if extra25519.IsEdLowOrder(keyRaw) { 123 return nil, fmt.Errorf("low order public key usage is forbidden for key '%s, try to generate a new one to fix the issue", key) 124 } 125 126 // Copy the public key 127 var pk [publicKeySize]byte 128 copy(pk[:], keyRaw[:publicKeySize]) 129 130 // Append it to sealing keys 131 res = append(res, &pk) 132 } 133 134 // No error 135 return res, nil 136 }